14

Here is my current scenario.

  • I am using AWS Elasticbeanstalk along with the eb cli 3.x tools for deployment.
  • I have created 2 environments (development and production). and one branch in my git repo for each environment (i.e. master , production)
  • I have created .ebextensions and .elasticbeanstalk folders in my git repo
  • the .ebextensions folder has config files that are specific to each environment (e.g. setups, files changes, environment variables . . etc)

I wish to work on each environment in its own git branch.

My difficulty

if i have to deploy to development env, it gets really simple

// make config changes in master branch
// git add, commit
// eb deploy
// thus development environment is updated

But if i have to deploy to production is where the problem begins

git checkout production
git merge master // pulls config that is meant for development environment only
eb deploy 

I want that when i merge changes from the master branch, all my code updates with the latest changes. But the .ebextensions and .elasticbeanstalk directories remain untouched

How do tell git to ignore the whole .ebextensions folder while merging into production branch ?

Lloyd
  • 183
  • 1
  • 5
  • So, you have the `.ebextensions` and `.elasticbeanstalk` folders in both the branches but when you merge them, you want both those folders to remain untouched ? – Noman Ur Rehman Mar 11 '15 at 12:24
  • yes. I need them specific to each branch. Since they represent the server configuration for each environment. Also i will always only merge from master to production. Never the reverse. Development will only happen in master – Lloyd Mar 11 '15 at 12:46
  • Probably useful: http://git-scm.com/book/en/v2/Customizing-Git-Git-Attributes#Merge-Strategies – Nick Humrich Mar 11 '15 at 16:10
  • 1
    Another possibility is to use saved configurations instead of ebextensions. "eb config save". – Nick Humrich Mar 11 '15 at 16:11

2 Answers2

20

Using EB CLI 3.x

For this version it's relatively simple. For example:

mkdir HelloWorld                 # create new directory for project
cd HelloWorld                    # enter the new directory
git init                         # create git repository
eb init -p PHP                   # create new application

echo "<?php echo getenv("ENV_NAME"); ?>" > index.php
git add .gitignore index.php
git commit -m 'Initial commit.'

eb create dev-env                # create environment named dev-env
eb create prod-env               # create environment named prod-env

eb use dev-env                   # associate dev-env to current branch (master)
eb setenv ENV_NAME=DEV           # set env variable specific to dev-env

git checkout -b production       # create production branch and switch to it
eb use prod-env                  # associate prod-env to the current branch (production)
eb setenv ENV_NAME=PROD          # set env variable specific to prod-env

This doesn't save ENV_NAME anywhere in the local filesystem. The EB CLI changes the live EB instance directly. You may use eb config save (as suggested by Nick Humrich) to save the environment configuration settings for the current running environment to .elasticbeanstalk/saved_configs/<env-name>.cfg.yml. Since each environment has its own file, you shouldn't have any conflicts, unless you change one of them in both branches. Another option (see Protecting sensitive informations) would be to add them to .gitignore.

Using EB CLI 2.x

Q: How did you create your environments?

One way is to have distinct optionsettings files for each environment (branch). The EB CLI can help you with that :-)

Run eb init from each branch (see below) and choose a different environment name for each one, so you'll end up with 2 distinct .elasticbeanstalk/optionsettings.<env-name> files. This way you avoid the conflicts on .elasticbeanstalk/.

1. Create the project directory

mkdir MyApp
cd MyApp

2. Initialize Git repository

git init .

3. Setup the development environment (master branch)

eb init

NOTE: When it asks you to provide an environment name, choose a name that identifies whether it's a development or production environment.

Enter your AWS Access Key ID (current value is "<redacted>"): 
Enter your AWS Secret Access Key (current value is "<redacted>"): 
Select an AWS Elastic Beanstalk service region.
Available service regions are:
<redacted>
Select (1 to 8): 1
Enter an AWS Elastic Beanstalk application name
(auto-generated value is "MyApp"): MyApp      
Enter an AWS Elastic Beanstalk environment name
(auto-generated value is "MyApp-env"): MyApp-dev
Select an environment tier.
Available environment tiers are:
1) WebServer::Standard::1.0
2) Worker::SQS/HTTP::1.0
Select (1 to 2): 1
Select a solution stack.
Available solution stacks are:
<redacted>
Select (1 to 59): 32
Select an environment type.
Available environment types are:
1) LoadBalanced
2) SingleInstance
Select (1 to 2): 2
Create an RDS DB Instance? [y/n]: n
Attach an instance profile (current value is "[Create a default instance profile]"):
<redacted>
Select (1 to 5): 4

4. Create a new branch for production

git checkout -b production

5. Setup the production environment

eb init

Repeat step 3 but pick a different environment name. This will create distinct .elasticbeanstalk/optionsettings.<env-name> file.

Q: What about my .ebextensions?

You should use the same app.config for both environments. The only thing that may diverge between environments is the option_settings section. But as far as I know, you can't have different option_settings per environment, so how can we do it?

Well, that's something I don't have an optimal solution yet, but I'll tell you how I do it. I add all option_name's I need and use placeholder values, for example:

option_settings:
  - option_name: MY_CONFIG
    value: CHANGEME

Then later I change their values manually through the AWS Elastic Beanstalk admin panel. Go to Application > Configuration > Software Configuration > Environment Properties.

Another possibility would be to have a custom script which is run by your container_commands. This script could identify the EC2 instance by its hostname (or another unique value) and automatically load the environment variables (e.g. source <hostname>.env).

Protecting sensitive informations

The only rule you need to obey is this: Your repository MUST NOT contain sensitive informations like credentials, unless you don't care.

For example, an application expects to read RDS credentials via environment variables, so you put them in option_settings. But you don't want other contributors to see them, do you? The solution I propose using placeholder is convenient on this aspect.

jweyrich
  • 31,198
  • 5
  • 66
  • 97
  • Sounds like your talking about the older CLI version (2.x) but the OP said they are using 3.x – Nick Humrich Mar 11 '15 at 16:07
  • @NickHumrich you're right. Thank you for the alert! I updated my answer to cover version 3.x. – jweyrich Mar 11 '15 at 18:02
  • Not sure if this helps. I have slightly different setups for dev and prod nvironments (not just the environment variables ) e.g. in prod env i have modified php to use memcached on another ec2 instance for sessions. While in dev env im using local memcached for sessions. Can this be handled by `eb config save` ? – Lloyd Mar 12 '15 at 05:42
  • @Lloyd: if you're only using a different host/port to connect to your memcached, you don't need different PHP scripts to handle that. You should put these **settings** in environment variables on your optionsettings files. Instead of having hard-coded host/port in your PHP script, make it read the environment variable you created and use it to connect to memcached. Would it solve the problem? – jweyrich Mar 12 '15 at 14:33
  • @Lloyd: Now if your _machine setup_ differs, then it's a bit harder. EB CLI could make this work per-environment, as it does for optionsettings files, but unfortunately it does not, at least currently. If between environments only container_commands differ, you may use the test option to determine if it should run the command in the current environment, for example: test: '[ ! $ENV_NAME == "PROD" ]'. This will refrain the respective command from running if the $ENV_NAME env variable is not equal to PROD. – jweyrich Mar 12 '15 at 14:35
  • @Lloyd: Then the problem is that packages/files/etc don't have the same test option. If you need these to be different, then we'll have to find another solution. FWIW, [Amazon is aware of this](https://forums.aws.amazon.com/message.jspa?messageID=529700) (thread from March, 2014). – jweyrich Mar 12 '15 at 14:35
  • 1
    @jweyrich : everything you have mentioned is correct. After a lot of searching i realized that this feature is not provided by amazon. even came across the same thread you pointed out. For now due to lack of time i have moved the env variables to the aws console so that the files do not have to be different. As for other configs i have temporarily just made the environments more or less similar. Will look at a better fix soon. Thank you :) – Lloyd Mar 12 '15 at 15:02
  • What happens when I take out a new branch from production/development? Where does the eb deploy to when I push code from this new branch? – Nirav Gandhi Oct 12 '17 at 08:21
1

You can merge master to production and production to master. However, after every merge, you have to reset the working tree of the folders in question. Here is how you can do this.

git checkout production
git merge --no-commit --no-ff master
git reset /path/to/[.ebextensions and .elasticbeanstalk]/folders
git commit
git push

Also, you may forget to reset after every merge so I suggest you set up a post-merge hook to do this automatically for the respective branches after every merge.

Noman Ur Rehman
  • 6,707
  • 3
  • 24
  • 39