Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Rails App Deployment with CodeDeploy
Search
tannai
August 30, 2016
Programming
1.6k
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Rails App Deployment with CodeDeploy
http://classmethod.connpass.com/event/38180/
tannai
August 30, 2016
More Decks by tannai
See All by tannai
redash patche at dmm
yuukigoodman
0
760
akibago-2018-10-30
yuukigoodman
0
84
serverless-design-and-streaming-date-processing-service
yuukigoodman
0
1k
alexa-changes-development-process
yuukigoodman
0
1.6k
VUIとAlexaによるちょっと未来の体験の話2
yuukigoodman
0
900
regrowth2016alexa
yuukigoodman
0
1.3k
cognito-userpools-in-production
yuukigoodman
4
8.8k
aws-lambda-in-practice
yuukigoodman
2
2.1k
serverless-from-today
yuukigoodman
2
2.2k
Other Decks in Programming
See All in Programming
Technical Debt: Understanding it Rightly, Engaging it Rightly #LaravelLiveJP
shogogg
0
210
Old Dog, New Tricks: The Java 25 Reinvention - JNation
bazlur_rahman
0
150
肥大化するレガシーコードに立ち向かうためのインターフェース分離と依存の逆転 / JJUG CCC 2026 Spring
hirokunimaeta
0
530
AutonomyとControlのあいだ:Graflowで記述するAIエージェント協調
myui
0
120
代数的データ型って何が嬉しいの? #frontend_phpcon_do
kajitack
8
3.3k
ふつうのFeature Flag実践入門
irof
7
3.7k
LLM Plugin for Node-REDの利用方法と開発について
404background
0
170
Language Server 使ってる? 〜VSCode と Zed の場合〜 / Are you using a Language Server? ~For VS Code and Zed~
handlename
0
780
ADKを使って簡単にAIエージェントを作ってみよう
k1mu21
0
250
タクシーアプリ『GO』の バックエンド開発のおける AI利活用と若者のすべて
pyama86
3
1.9k
エージェンティックRAGにAWSで入門しよう!
har1101
8
1.4k
DynamoDBには集計系のクエリがないけどなんとかしたい
musan
1
130
Featured
See All Featured
How to Build an AI Search Optimization Roadmap - Criteria and Steps to Take #SEOIRL
aleyda
1
2.1k
ReactJS: Keep Simple. Everything can be a component!
pedronauck
666
130k
Practical Tips for Bootstrapping Information Extraction Pipelines
honnibal
25
1.9k
Building the Perfect Custom Keyboard
takai
2
790
Product Roadmaps are Hard
iamctodd
PRO
55
12k
Game over? The fight for quality and originality in the time of robots
wayneb77
1
190
Fight the Zombie Pattern Library - RWD Summit 2016
marcelosomers
234
17k
The Power of CSS Pseudo Elements
geoffreycrofte
82
6.3k
The Psychology of Web Performance [Beyond Tellerrand 2023]
tammyeverts
49
3.5k
Building Better People: How to give real-time feedback that sticks.
wjessup
370
20k
The AI Revolution Will Not Be Monopolized: How open-source beats economies of scale, even for LLMs
inesmontani
PRO
3
3.5k
Designing Experiences People Love
moore
143
24k
Transcript
RailsΞϓϦΛ σϓϩΠ͢Δ CodeDeploy Classmethod, Inc. Yuki Tannai ࣮ફAWSϓϩάϥϛϯά
ࣗݾհ • ୮༏ل • @yuukigoodman • αʔόͰಈ͘ϓϩάϥϜͱ͔AWS
Agenda • σϓϩΠͱ • σϓϩΠΛࢧ͑ΔAWS • CodeDeployͰRailsΛσϓϩΠ
σϓϩΠͱ
γεςϜΛ ར༻Մೳʹ͢Δ ׆ಈશൠͰ͋Δ — ja.wikipedia.org1 1 https://ja.wikipedia.org/wiki/ιϑτΣΞσϓϩΠϝϯτ
• ͨͩίʔυΛμϯϩʔυͯ͠ىಈ͢Εྑ͍ ͱ͍͏ͷͰͳ͍ • σϓϩΠʹΑͬͯղܾ͍͕ͨ͋͠Γɺ ͦΕϓϩδΣΫτͷਐߦͱڞʹมԽ͢Δ • σϓϩΠαʔϏεఏڙऀͷΛղܾ͢Δ • ఏڙऀͷղܾར༻ऀͷՁʹͳΓಘΔ
• σϓϩΠ࣭ͷͻͱͭ(QCD) • Quality/Cost/Delivery
σϓϩΠࣗಈԽͷಈػ • ස͕ଟ͍ • ϛε͕Ͱ͖ͳ͍ • ϧʔνϯϫʔΫ • ͰʑมΘΔ ࣗಈԽ͠ͳ͍ͱͬͯΒΜͶʔʂʂ
σϓϩΠࣗಈԽͷ؍ • Ͳ͔͜ΒͲ͜ʹσϓϩΠ͢Δ͔ • ࣗಈσϓϩΠͷ࣮ߦτϦΨʔ • ͍ͭ୭͕σϓϩΠ͢Δ͔ • ࣦഊΛͲ͏ݕग़͠ɺͲ͜ʹ௨͢Δ͔ •
Ͳͷ͘Β͍มߋ͕ೖΔ͔
σϓϩΠϓϩδΣΫτͷਐߦͱͱʹมԽ͢Δ • Ϗδωε: ༷มߋೲظॖ • ৫: ϝϯόʔͷೖΕସ͑૿ݮɺख़ͷෆ ۉҰ͞ • ٕज़:
όʔδϣϯΞοϓύϑΥʔϚϯε ͜ΕΒͷཧ༝ʹΑΓσϓϩΠมߋ͞ΕΔ
มߋʹ͑͏Δ σϓϩΠࣗಈԽͷ ϓϩάϥϜΛॻ͘
ͦͷͨΊͷඪઃఆͱઃܭ͕େࣄ
σϓϩΠΛࢧ͑Δ AWS
None
EC2 • ΦʔιυοΫεͳσϓϩΠλʔήοτ • େͷ߹EC2ʹσϓϩΠ͢Δ • Πϯελϯε࡞ͱϓϩϏδϣχϯά͕ඞཁ
None
Elastic Beanstalk • HerokuΈ͍ͨͳαʔϏε • EC2/RDS/ELBϦιʔεΛ؆୯ʹཧ͢Δ • ࣮ମCFn • ઐ༻ͷίϚϯυ(eb-cli)͕͋Δ
• eb deploy
None
Cloud Formation • AWSϦιʔεͷߏཧ • ϒϥβCLIͰ͍ͬͯΔ͜ͱΛJSONͰهड़ ͢Δͱ࣮ߦͯ͘͠ΕΔ • AWSެࣜఏڙπʔϧͳͷͰ҆৺ •
࠶ݱੑ͕ٻΊΒΕΔ߹ඇৗʹ༗ޮ
None
EC2 Container Service • EC2Ϋϥελ্Ͱͷίϯςφ࣮ߦΛཧ • Dockerӡ༻ʹৄ͍͠ਓɺ ͥͻ࠙ձͰڭ͍͑ͯͩ͘͞ >< •
Container Registry౦ژʹདྷͨ
None
Code Deploy • EC2ͷίʔυͷஔͱىಈΛཧ͢ΔαʔϏε • ຊͷओਓެ • EC2ʹAgentΛΠϯετʔϧ͓ͯ͘͠
CodeDeployͰ RailsΛσϓϩΠ
େ·͔ͳྲྀΕ • Ansible Playbookͷ࡞ • CodeDeployઃఆͷ࡞ • Cloud Formation Templateͷ࡞
• σϓϩΠͯ͠ΈΔ • Ճཁ݅Λ࣮͢Δ • ࣦഊ࣌ͷ௨ • Blue Green Deployment
Ansible Playbookͷ࡞
webservers.yml - hosts: all remote_user: ec2-user vars: rails_env: "{{ lookup('env',
'RAILS_ENV') }}" roles: - common - nginx - fluentd - logrotate - ruby - node - rails - ssh
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
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
roles/node/tasks/main.yml - name: install nodejs yum: name=nodejs enablerepo=epel sudo: yes
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
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
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"
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"
CodeDeployઃఆͷ࡞
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
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
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
ApplicationStop • σϓϩΠલʹΞϓϦΛఀࢭ͢Δ • ELBԼͷ߹ొղআ͢Δ • EC2ࣗΛstand byʹ͢Δ • deregister_from_elb.sh
awslabsϦϙͰ
ApplicationStop • application_stop.shͰదٓRailsΛఀࢭ͢ Δ • ͜ͷ࣌ͰELB͔Βִ͞Ε͍ͯΔͷͰࢭΊ ͳͯ͘ྑ͍
BeforeInstall • ιʔείʔυΛEC2ʹίϐʔ͢Δ • ओʹAnsible(ͱCapistrano)ͷ४උ • appspec.ymlͰrunas: rootΛΕͳ͍
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
AfterInstall • ίϐʔͱunzipͷ࣍ʹ࣮ߦ͞ΕΔ • ࣗͷEC2λάΛಡΜͰઃఆϑΝΠϧΛμϯ ϩʔυͯ͠ධՁ
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
ApplicationStart • Railsͷىಈ४උͱىಈΛߦͳ͏ • ࠓճcapistranoͰٵऩ͍ͯ͠Δ
ApplicationStart #!/bin/bash set -u . /etc/profile.d/rbenv.sh . $APPLICATION_VARIABLES_PATH cd $DESTINATION_PATH
rbenv exec bundle exec cap local deploy
ValidateService • ΞϓϦέʔγϣϯͷىಈͱELBొΛߦͳ͏ • ҎԼͷεΫϦϓτ͕exit code 0ͳΒ࣍ͷ registerwithelb.sh͕Δ • registerwithelb.shAWSLabsͰެ։͞
Ε͍ͯΔ #!/bin/bash curl -s http://localhost/health_check
Cloud Formation Templateͷ࡞
ͦΕΛه͢ʹ ༨ന͕Γͳ͍
ͬͯΔ͜ͱ • ELBͷ࡞ • Launch Configͷ࡞ • User DataͰCodeDeploy AgentͷΠϯε
τʔϧ • Auto Scaling Groupͷ࡞ • Route53Ϩίʔυͷ࡞ ※CodeDeployϦιʔε࡞ͯ͠ͳ͍
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" ] ] } },
CFn Create Stack
CodeDeployϦιʔεΛखಈͰ࡞ • ڥมshΛS3ʹஔ͘ • CFnͰͰ͖ͨASGΛࢦఆͯ͠Aplication/ DeploymentGroup࡞
σϓϩΠͯ͠ΈΔ
Ճཁ݅: ΑΓҰͷࣗಈԽ
ྲྀΕ • εΫϦϓτͰσϓϩΠ༻zipΛ࡞ • S3ʹஔ • LambdaͰCodeDeploy
εΫϦϓτͰσϓϩΠ༻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()); });
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 } } };
S3ʹzipΛஔ͘ͱಈ͘Lambda codedeploy.createDeployment(params, (err, data) => { if (err) { console.log(err);
callback(err); } else { callback(null, data); } });
Ճཁ݅: ىಈΛૣ͘͢Δ
AMIΛͱΔ • σϓϩΠঢ়ଶͷAMI࡞ • CFnमਖ਼
Ճཁ݅: B-G σϓϩΠ
• ELB+ASGΛDeploymentGroupΛ2ͭ࡞ • CodeDeloyޭ࣌ʹSNSܦ༝ͰLambdaΛىಈ • LambdaͰRoute53 ALIASϨίʔυͷ͖ઌΛ ม͑Δ
·ͱΊ
CodeDeployͰҎԼΛୡՄೳ • AnsibleγΣϧεΫϦϓτͷφϨοδΛస༻ Ͱ͖Δ • χʔζʹ߹ΘͤͨσϓϩΠγεςϜΛ࡞ΕΔ