Hosting Ruby Web Apps

Michael Reinsch
Michael Reinschco-Founder at Doorkeeper Inc
Hosting Ruby Web
Apps
Lessons learned from 8 years of
Overview
• System architecture
• Initial setup & deploy
• Keep it running and moving forward
Michael Reinsch
michael@doorkeeper.jp
@mreinsch
Next Weekend!
startupweekend.jp/swtokyo-personal-cloud/
Michael Reinsch
michael@doorkeeper.jp
@mreinsch
Build a community through your events.
Hosting Ruby Web Apps
DB
(psql)
memcache
nginx
unicorn
unicornRails App
(unicorn)
job worker
MTA
(postfix)
Contenders
Contenders
Contenders
Contenders
Contenders
Please Note
• Not an exhaustive list of all hosting providers

(it’s not even everyone we’ve been using)
• Moving targets
Choosing Server Size
www.flickr.com/photos/jonrb/7864016624
Choosing Server Size
• good CPU performance
• choose 2GB RAM or more
Choosing Server Size
• 1 ECU/core is a bit frustrating
• 2 ECU/core is OK
• >2 ECU/core is better
Choosing Server Size
• 3 different sizes
• choose standard size if you don’t
have specific requirements
Hardware Failures
How can we make things robust?
www.flickr.com/photos/doegox/4551458930
Hardware Failures
“We’re trying to prevent failures, please
make backups for worst case”
!
• provides load balancer
• fault tolerant setup example:

2 web instances + 2 DB instances
• need to configure DB replication and
failover yourself
Hardware Failures
“Failures will happen, build your infrastructure
so they won’t impact you”
• provides load balancer (ELB)
• provides managed DB instances (RDS)
• replication support (not for psql yet)
• DB snapshots (can’t download though)
• fault tolerant setup example: 

2 web instances + multi-AZ RDS
Hardware Failures
“Failures will happen, let us help you build an
infrastructure so they won’t impact you”
• HA proxy on web instances
• one-click setup for DB (mysql/psql)
• replication support
• DB snapshots (downloadable)
• fault tolerant setup example:

2 web instances + 2 DB instances
Hardware Failures
“You don’t need to worry about failures”
!
!
• everything managed
• fault tolerant setup example:

2 dynos + premium DB (psql)
Initial Setup & Deploy
www.flickr.com/photos/thedailyenglishshow/6013713229
Initial Setup & Deploy
• manual setup feasible:
• install OS, ruby (rvm), libs, DB
• setup nginx
Initial Setup & Deploy
• let’s choose Capistrano for deploying
• Capistrano config goes into source
repository
• Initial deploy:
cap deploy:setup

cap deploy:cold
Initial Setup & Deploy
• let’s choose OpsWorks
• based on chef, provides predefined
set of recipes
• more high level than Cloud Formation,
more flexible than Elastic Beanstalk
Initial Setup & Deploy
• initial deploy:
• create stack
• define layers
• create instances
• create app
• deploy
Initial Setup & Deploy
• define base layer
• assign it to all instances
• use it for any common recipes like
creating swap, NewRelic, ...
Tip
Initial Setup & Deploy
• you need to handle asset compilation
• use deploy hook:
Tip
# deploy/before_migrate.rb
!
rails_env = new_resource.environment["RAILS_ENV"]
Chef::Log.info("Precompiling assets for RAILS_ENV=#{rails_env}...")
!
execute "rake assets:precompile" do
cwd release_path
command "bundle exec rake assets:precompile"
environment "RAILS_ENV" => rails_env
end
!
!
Initial Setup & Deploy
• provides toolchain based on chef
• initial deploy:
• create an application
• select environment layout
• add plugins (NewRelic, ...)
• deploy
Initial Setup & Deploy
Heroku comes with its’ own toolchain:
heroku create my-awesome-app

heroku addons:add …

git remote add heroku …

git push heroku master
What you’ll probably also
need
• App configuration 

(for secrets and endpoints)
• Sending email
• Job queue
App Configuration
• no predefined way: DIY
• put settings.yml into shared config dir
• link it into app when deploying
App Configuration
• uses environment variables
heroku config:add MY_SECRET=topsecret

heroku config
• in your code:
ENV[‘MY_SECRET’]
Sending Email
• use an email sending service

(SendGrid, AWS SNS)
• EY and Heroku provide plugins for
easy installation
Sending Email Yourself
• reverse IP lookup
• port 25 is restricted
• make sure your IP isn’t blacklisted
Job Queue
• DIY
• define custom layer in OpsWorks,
need to get 3rd party recipe
Job Queue
• sample recipes are provided
• worker instances
Job Queue
• provides worker dynos
• requires setup in Procfile
Background
mailer
• use database transaction to atomically create
mailer job and related data
• rollback if something goes wrong
• reduces request response time
• job can retry sending email
Tip
Up and running :-)
Continuous Deployment
www.flickr.com/photos/layos/3743880081
Continuous Deployment
• keep deploys cheap
• automate deploy
• easy deployment trigger
• good test coverage - use CI
• use rolling / zero downtime deploy
Continuous Deployment
• deploy command:
git pull

cap deploy
• unicorn for rolling deploy
Continuous Deployment
• unicorn is configured for rolling
deploys
• deploy command:
aws --region=‘us-east-1’

opsworks create-deployment

--stack-id=‘<STACK_UUID>’

--app-id=‘<APP_UUID>’

--instance-ids=‘[“<INSTANCE_UUID>”]’

--command='{"Name": "deploy"}
Continuous Deployment
gem 'aws-sdk'
gem 'parseconfig'
!
PRODUCTION_APP_ID = "09afbde1-322b-4816-a1e9-xxxxxxxxxxxx"
!
config = ParseConfig.new(File.expand_path("~/.aws/config"))
AWS.config(
:access_key_id => config['default']['aws_access_key_id'],
:secret_access_key => config['default']['aws_secret_access_key'])
!
@ops = AWS::OpsWorks.new.client
@ops_app = @ops.describe_apps(app_ids: [PRODUCTION_APP_ID])[:apps].first
@ops_stack = @ops.describe_stack_summary(stack_id: @ops_app[:stack_id])[:stack_summary]
@ops_inst = @ops.describe_instances(stack_id: @ops_app[:stack_id])[:instances]
@ops_inst_ids = @ops_instances.map do |instance|
instance[:instance_id] if instance[:status] == 'online'
end.compact
!
puts "Deploying #{@ops_app[:name]} to #{@ops_stack[:name]}"
!
deploy_options = {
command: { name:' deploy' },
comment: "deploy from #{Socket.gethostname}",
stack_id: @ops_app[:stack_id],
app_id: @ops_app[:app_id],
instance_ids: @ops_inst_ids }
@ops.create_deployment deploy_options
!
!
or some simple ruby script:
Continuous Deployment
• deploy command:
ey deploy
• in config/ey.yml:
maintenance_on_migrate: false
Continuous Deployment
• deploy command:
git pull

git push heroku master
• no-downtime deploys experimental:
heroku labs:enable -a myapp preboot
Continuous Deployment
• “fork” in code
• on/off switch for features
• slow rollout of new features
Tip
Rolling Deploys with!
Database Migrations
www.flickr.com/photos/edwardshousemovers/6704586649
Rolling Deploys with
Database Migrations
orchestrated deployment flow:
1. copy code, keep old version
2. run migrations
3. switch to new code
➜ one-step deployments
Rolling Deploys with
Database Migrations
• asynchronous deployment flow
• two-step deployment
1. deploy old code + DB migrations

(one instance only)
2. deploy new code

(all instances)
Rolling Deploys with
Database Migrations
• migrations are run via:
heroku run rake db:migrate
• two-step deployment
1. deploy old code + DB migrations
2. deploy new code
Down for!
Maintenance
www.flickr.com/photos/metrolibraryarchive/4128611963
Down for Maintenance
• Maintenance page done right:
• include contact and progress info

(use 3rd party service like Twitter)
• serve page with 503 error code
• use asset host or inline all assets
Tip
Down for Maintenance
• add ‘capistrano-maintenance’ gem
• configure nginx
• then:
cap maintenance:enable
server { // nginx server config
!
location @maintenance {
rewrite ^(.*)$ /system/maintenance.html last;
break;
}
if (-f $document_root/system/maintenance.html) { return 503; }
!
error_page 503 @maintenance;
Down for Maintenance
• Need custom recipe / script
• Can use similar approach as with
Capistrano
• 503 won’t pass ELB health check!
• Alternative: use Route53 to fail over to
instance serving maintenance page
Down for Maintenance
• Part of the platform:
ey web disable
heroku maintenance:on
Getting Support
Getting Support
• provides ticket system for any
platform related issues
• forum for anything else
Getting Support
• forums for everything
• access to tickets only available if
certain health checks fail
Getting Support
• tickets for everything
• provides even hosting related help on
application level
• “extension of your team”
Conclusion
• there is no silver bullet
• a lot depends on your app
• be ready to get your hands dirty
• your production environment is your
app
• balance ops / dev
Thank you!
Contact:
Michael Reinsch

michael@doorkeeper.jp

@mreinsch
Questions?
More awesome events:
• ijaws.doorkeeper.jp
• Startup Weekend Tokyo Personal Cloud
1 of 65

Recommended

Inside the Chef Push Jobs Service - ChefConf 2015 by
Inside the Chef Push Jobs Service - ChefConf 2015 Inside the Chef Push Jobs Service - ChefConf 2015
Inside the Chef Push Jobs Service - ChefConf 2015 Chef
2.5K views33 slides
Mitchell Hashimoto, HashiCorp by
Mitchell Hashimoto, HashiCorpMitchell Hashimoto, HashiCorp
Mitchell Hashimoto, HashiCorpOntico
12.3K views97 slides
Chef Fundamentals Training Series Module 1: Overview of Chef by
Chef Fundamentals Training Series Module 1: Overview of ChefChef Fundamentals Training Series Module 1: Overview of Chef
Chef Fundamentals Training Series Module 1: Overview of ChefChef Software, Inc.
37.4K views61 slides
Zero Code Multi-Cloud Automation with Ansible and Terraform by
Zero Code Multi-Cloud Automation with Ansible and TerraformZero Code Multi-Cloud Automation with Ansible and Terraform
Zero Code Multi-Cloud Automation with Ansible and TerraformAvi Networks
923 views28 slides
Distributed automation sel_conf_2015 by
Distributed automation sel_conf_2015Distributed automation sel_conf_2015
Distributed automation sel_conf_2015aragavan
12.8K views41 slides
Puppet in the Pipeline by
Puppet in the PipelinePuppet in the Pipeline
Puppet in the PipelinePuppet
1.3K views63 slides

More Related Content

What's hot

Immutable Infrastructure with Packer Ansible and Terraform by
Immutable Infrastructure with Packer Ansible and TerraformImmutable Infrastructure with Packer Ansible and Terraform
Immutable Infrastructure with Packer Ansible and TerraformMichael Peacock
209 views51 slides
Learning chef by
Learning chefLearning chef
Learning chefJonathan Carrillo
927 views70 slides
What's new in chef 12 by
What's new in chef 12 What's new in chef 12
What's new in chef 12 Charles Johnson
4.6K views20 slides
Compliance as Code by
Compliance as CodeCompliance as Code
Compliance as CodeMatt Ray
2.3K views70 slides
Scaling and Managing Selenium Grid by
Scaling and Managing Selenium GridScaling and Managing Selenium Grid
Scaling and Managing Selenium Griddimakovalenko
6.7K views46 slides
Introduction to Chef: Automate Your Infrastructure by Modeling It In Code by
Introduction to Chef: Automate Your Infrastructure by Modeling It In CodeIntroduction to Chef: Automate Your Infrastructure by Modeling It In Code
Introduction to Chef: Automate Your Infrastructure by Modeling It In CodeJosh Padnick
16.6K views99 slides

What's hot(20)

Immutable Infrastructure with Packer Ansible and Terraform by Michael Peacock
Immutable Infrastructure with Packer Ansible and TerraformImmutable Infrastructure with Packer Ansible and Terraform
Immutable Infrastructure with Packer Ansible and Terraform
Michael Peacock209 views
Compliance as Code by Matt Ray
Compliance as CodeCompliance as Code
Compliance as Code
Matt Ray2.3K views
Scaling and Managing Selenium Grid by dimakovalenko
Scaling and Managing Selenium GridScaling and Managing Selenium Grid
Scaling and Managing Selenium Grid
dimakovalenko6.7K views
Introduction to Chef: Automate Your Infrastructure by Modeling It In Code by Josh Padnick
Introduction to Chef: Automate Your Infrastructure by Modeling It In CodeIntroduction to Chef: Automate Your Infrastructure by Modeling It In Code
Introduction to Chef: Automate Your Infrastructure by Modeling It In Code
Josh Padnick16.6K views
Push jobs: an orchestration building block for private Chef by Chef Software, Inc.
Push jobs: an orchestration building block for private ChefPush jobs: an orchestration building block for private Chef
Push jobs: an orchestration building block for private Chef
Chef Software, Inc.8.3K views
Using SaltStack to DevOps the enterprise by Christian McHugh
Using SaltStack to DevOps the enterpriseUsing SaltStack to DevOps the enterprise
Using SaltStack to DevOps the enterprise
Christian McHugh495 views
Autoscaled Distributed Automation using AWS at Selenium London MeetUp by aragavan
Autoscaled Distributed Automation using AWS at Selenium London MeetUpAutoscaled Distributed Automation using AWS at Selenium London MeetUp
Autoscaled Distributed Automation using AWS at Selenium London MeetUp
aragavan1.8K views
The way to set automation testing by Duy Tan Geek
The way to set automation testingThe way to set automation testing
The way to set automation testing
Duy Tan Geek854 views
SAP TechEd 2013 session Tec118 managing your-environment by Chris Kernaghan
SAP TechEd 2013 session Tec118 managing your-environmentSAP TechEd 2013 session Tec118 managing your-environment
SAP TechEd 2013 session Tec118 managing your-environment
Chris Kernaghan1.7K views
Automating aws infrastructure and code deployments using Ansible @WebEngage by Vishal Uderani
Automating aws infrastructure and code deployments using Ansible @WebEngageAutomating aws infrastructure and code deployments using Ansible @WebEngage
Automating aws infrastructure and code deployments using Ansible @WebEngage
Vishal Uderani1.1K views
Infrastructure modeling with chef by Charles Johnson
Infrastructure modeling with chefInfrastructure modeling with chef
Infrastructure modeling with chef
Charles Johnson805 views
How Parse Built a Mobile Backend as a Service on AWS (MBL307) | AWS re:Invent... by Amazon Web Services
How Parse Built a Mobile Backend as a Service on AWS (MBL307) | AWS re:Invent...How Parse Built a Mobile Backend as a Service on AWS (MBL307) | AWS re:Invent...
How Parse Built a Mobile Backend as a Service on AWS (MBL307) | AWS re:Invent...
Amazon Web Services10.3K views
Chef vs Puppet vs Ansible vs SaltStack | Configuration Management Tools Compa... by Edureka!
Chef vs Puppet vs Ansible vs SaltStack | Configuration Management Tools Compa...Chef vs Puppet vs Ansible vs SaltStack | Configuration Management Tools Compa...
Chef vs Puppet vs Ansible vs SaltStack | Configuration Management Tools Compa...
Edureka!5.7K views
Infrastructure Automation with Chef & Ansible by wajrcs
Infrastructure Automation with Chef & AnsibleInfrastructure Automation with Chef & Ansible
Infrastructure Automation with Chef & Ansible
wajrcs2.6K views

Similar to Hosting Ruby Web Apps

Ansible: How to Get More Sleep and Require Less Coffee by
Ansible: How to Get More Sleep and Require Less CoffeeAnsible: How to Get More Sleep and Require Less Coffee
Ansible: How to Get More Sleep and Require Less CoffeeSarah Z
25.5K views64 slides
Moving Windows Applications to the Cloud by
Moving Windows Applications to the CloudMoving Windows Applications to the Cloud
Moving Windows Applications to the CloudRightScale
2K views31 slides
Configuration Management in the Cloud - AWS Online Tech Talks by
Configuration Management in the Cloud - AWS Online Tech TalksConfiguration Management in the Cloud - AWS Online Tech Talks
Configuration Management in the Cloud - AWS Online Tech TalksAmazon Web Services
1.4K views48 slides
Getting to Walk with DevOps by
Getting to Walk with DevOpsGetting to Walk with DevOps
Getting to Walk with DevOpsEklove Mohan
330 views17 slides
Ansible presentation by
Ansible presentationAnsible presentation
Ansible presentationSuresh Kumar
7.1K views21 slides
Ansible benelux meetup - Amsterdam 27-5-2015 by
Ansible benelux meetup - Amsterdam 27-5-2015Ansible benelux meetup - Amsterdam 27-5-2015
Ansible benelux meetup - Amsterdam 27-5-2015Pavel Chunyayev
875 views38 slides

Similar to Hosting Ruby Web Apps(20)

Ansible: How to Get More Sleep and Require Less Coffee by Sarah Z
Ansible: How to Get More Sleep and Require Less CoffeeAnsible: How to Get More Sleep and Require Less Coffee
Ansible: How to Get More Sleep and Require Less Coffee
Sarah Z25.5K views
Moving Windows Applications to the Cloud by RightScale
Moving Windows Applications to the CloudMoving Windows Applications to the Cloud
Moving Windows Applications to the Cloud
RightScale2K views
Configuration Management in the Cloud - AWS Online Tech Talks by Amazon Web Services
Configuration Management in the Cloud - AWS Online Tech TalksConfiguration Management in the Cloud - AWS Online Tech Talks
Configuration Management in the Cloud - AWS Online Tech Talks
Amazon Web Services1.4K views
Getting to Walk with DevOps by Eklove Mohan
Getting to Walk with DevOpsGetting to Walk with DevOps
Getting to Walk with DevOps
Eklove Mohan330 views
Ansible presentation by Suresh Kumar
Ansible presentationAnsible presentation
Ansible presentation
Suresh Kumar7.1K views
Ansible benelux meetup - Amsterdam 27-5-2015 by Pavel Chunyayev
Ansible benelux meetup - Amsterdam 27-5-2015Ansible benelux meetup - Amsterdam 27-5-2015
Ansible benelux meetup - Amsterdam 27-5-2015
Pavel Chunyayev875 views
Continuous Delivery: How RightScale Releases Weekly by RightScale
Continuous Delivery: How RightScale Releases WeeklyContinuous Delivery: How RightScale Releases Weekly
Continuous Delivery: How RightScale Releases Weekly
RightScale920 views
PyCon India 2012: Celery Talk by Piyush Kumar
PyCon India 2012: Celery TalkPyCon India 2012: Celery Talk
PyCon India 2012: Celery Talk
Piyush Kumar4.4K views
Auto Deploy Deep Dive – vBrownBag Style by Robert Nelson
Auto Deploy Deep Dive – vBrownBag StyleAuto Deploy Deep Dive – vBrownBag Style
Auto Deploy Deep Dive – vBrownBag Style
Robert Nelson10.1K views
Training Slides: 103 - Basics - Simple Tungsten Clustering Installation by Continuent
Training Slides: 103 - Basics - Simple Tungsten Clustering InstallationTraining Slides: 103 - Basics - Simple Tungsten Clustering Installation
Training Slides: 103 - Basics - Simple Tungsten Clustering Installation
Continuent85 views
OpenStack Deployments with Chef by Matt Ray
OpenStack Deployments with ChefOpenStack Deployments with Chef
OpenStack Deployments with Chef
Matt Ray6.4K views
Sergey Dzyuban "To Build My Own Cloud with Blackjack…" by Fwdays
Sergey Dzyuban "To Build My Own Cloud with Blackjack…"Sergey Dzyuban "To Build My Own Cloud with Blackjack…"
Sergey Dzyuban "To Build My Own Cloud with Blackjack…"
Fwdays341 views
Deployment automation framework with selenium by Wenhua Wang
Deployment automation framework with seleniumDeployment automation framework with selenium
Deployment automation framework with selenium
Wenhua Wang3K views
7 steps to simplifying your AI workflows by Wisecube AI
7 steps to simplifying your AI workflows7 steps to simplifying your AI workflows
7 steps to simplifying your AI workflows
Wisecube AI82 views
OSDC 2013 | Introduction into Chef by Andy Hawkins by NETWAYS
OSDC 2013 | Introduction into Chef by Andy HawkinsOSDC 2013 | Introduction into Chef by Andy Hawkins
OSDC 2013 | Introduction into Chef by Andy Hawkins
NETWAYS28 views
DjangoCon 2010 Scaling Disqus by zeeg
DjangoCon 2010 Scaling DisqusDjangoCon 2010 Scaling Disqus
DjangoCon 2010 Scaling Disqus
zeeg32.6K views

Recently uploaded

Five Things You SHOULD Know About Postman by
Five Things You SHOULD Know About PostmanFive Things You SHOULD Know About Postman
Five Things You SHOULD Know About PostmanPostman
36 views43 slides
PRODUCT LISTING.pptx by
PRODUCT LISTING.pptxPRODUCT LISTING.pptx
PRODUCT LISTING.pptxangelicacueva6
14 views1 slide
Zero to Automated in Under a Year by
Zero to Automated in Under a YearZero to Automated in Under a Year
Zero to Automated in Under a YearNetwork Automation Forum
15 views23 slides
Democratising digital commerce in India-Report by
Democratising digital commerce in India-ReportDemocratising digital commerce in India-Report
Democratising digital commerce in India-ReportKapil Khandelwal (KK)
18 views161 slides
【USB韌體設計課程】精選講義節錄-USB的列舉過程_艾鍗學院 by
【USB韌體設計課程】精選講義節錄-USB的列舉過程_艾鍗學院【USB韌體設計課程】精選講義節錄-USB的列舉過程_艾鍗學院
【USB韌體設計課程】精選講義節錄-USB的列舉過程_艾鍗學院IttrainingIttraining
58 views8 slides
SAP Automation Using Bar Code and FIORI.pdf by
SAP Automation Using Bar Code and FIORI.pdfSAP Automation Using Bar Code and FIORI.pdf
SAP Automation Using Bar Code and FIORI.pdfVirendra Rai, PMP
23 views38 slides

Recently uploaded(20)

Five Things You SHOULD Know About Postman by Postman
Five Things You SHOULD Know About PostmanFive Things You SHOULD Know About Postman
Five Things You SHOULD Know About Postman
Postman36 views
【USB韌體設計課程】精選講義節錄-USB的列舉過程_艾鍗學院 by IttrainingIttraining
【USB韌體設計課程】精選講義節錄-USB的列舉過程_艾鍗學院【USB韌體設計課程】精選講義節錄-USB的列舉過程_艾鍗學院
【USB韌體設計課程】精選講義節錄-USB的列舉過程_艾鍗學院
SAP Automation Using Bar Code and FIORI.pdf by Virendra Rai, PMP
SAP Automation Using Bar Code and FIORI.pdfSAP Automation Using Bar Code and FIORI.pdf
SAP Automation Using Bar Code and FIORI.pdf
iSAQB Software Architecture Gathering 2023: How Process Orchestration Increas... by Bernd Ruecker
iSAQB Software Architecture Gathering 2023: How Process Orchestration Increas...iSAQB Software Architecture Gathering 2023: How Process Orchestration Increas...
iSAQB Software Architecture Gathering 2023: How Process Orchestration Increas...
Bernd Ruecker40 views
Automating a World-Class Technology Conference; Behind the Scenes of CiscoLive by Network Automation Forum
Automating a World-Class Technology Conference; Behind the Scenes of CiscoLiveAutomating a World-Class Technology Conference; Behind the Scenes of CiscoLive
Automating a World-Class Technology Conference; Behind the Scenes of CiscoLive
Business Analyst Series 2023 - Week 3 Session 5 by DianaGray10
Business Analyst Series 2023 -  Week 3 Session 5Business Analyst Series 2023 -  Week 3 Session 5
Business Analyst Series 2023 - Week 3 Session 5
DianaGray10300 views
PharoJS - Zürich Smalltalk Group Meetup November 2023 by Noury Bouraqadi
PharoJS - Zürich Smalltalk Group Meetup November 2023PharoJS - Zürich Smalltalk Group Meetup November 2023
PharoJS - Zürich Smalltalk Group Meetup November 2023
Noury Bouraqadi132 views
Voice Logger - Telephony Integration Solution at Aegis by Nirmal Sharma
Voice Logger - Telephony Integration Solution at AegisVoice Logger - Telephony Integration Solution at Aegis
Voice Logger - Telephony Integration Solution at Aegis
Nirmal Sharma39 views
TouchLog: Finger Micro Gesture Recognition Using Photo-Reflective Sensors by sugiuralab
TouchLog: Finger Micro Gesture Recognition  Using Photo-Reflective SensorsTouchLog: Finger Micro Gesture Recognition  Using Photo-Reflective Sensors
TouchLog: Finger Micro Gesture Recognition Using Photo-Reflective Sensors
sugiuralab21 views
Case Study Copenhagen Energy and Business Central.pdf by Aitana
Case Study Copenhagen Energy and Business Central.pdfCase Study Copenhagen Energy and Business Central.pdf
Case Study Copenhagen Energy and Business Central.pdf
Aitana16 views

Hosting Ruby Web Apps