README.md 11.5 KB
Newer Older
jarv committed
1
# Configuration Management
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36

## Introduction

**This project is currently in alpha**

The goal of the edx/configuration project is to provide a simple, but
flexible, way for anyone to stand up an instance of the edX platform
that is fully configured and ready-to-go.

Building the platform takes place to two phases:

* Infrastruce provisioning
* Service configuration

As much as possible, we have tried to keep a clean distinction between
provisioning and configuration.  You are not obliged to use our tools
and are free to use one, but not the other.  The provisioing phase 
stands-up the required resources and tags them with role identifiers
so that the configuration tool can come in and complete the job.

The reference platform is provisioned using an Amazon
[CloudFormation](http://aws.amazon.com/cloudformation/) template.
When the stack has been fully created you will have a new AWS Virtual
Private Cloud with hosts for the core edX services.  This template
will build quite a number of AWS resources that cost money, so please
consider this before you start.

The configuration phase is manged by [Ansible](http://ansible.cc/).
We have provided a number of playbooks that will configure each of
the edX service.  

This project is a re-write of the current edX provisioning and
configuration tools, we will be migrating features to this project
over time, so expect frequent changes.

jarv committed
37 38
## AWS

39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
### Building the stack

The first step is to provision the CloudFormation stack.  There are 
several options for doing this.

* The [AWS console](https://console.aws.amazon.com/cloudformation/home)
* The AWS [CloudFormation CLI](http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-installing-cli.html)
* Via Ansible

If you don't have experience with CloudFormation, the web console is a
good place to start because it will use a form wizard to gather
configuration parameters, it will give you continuous feedback during
the process of building the stack and useful error messages when
problems occur.

Details on how to build the stack using Ansible are available below.

### Connecting to Hosts in the Stack

Because the reference architecture makes use of an Amazon VPC, you will not be able
to address the hosts in the private subnets directly.  However, you can easily set 
up a transparent "jumpbox" so that for all hosts in your vpc, connections are 
tunneled.

Add something like the following to your `~/.ssh/config` file.

```
Host *.us-west-1.compute-internal
  ProxyCommand ssh -W %h:%p vpc-00000000-jumpbox
  IdentityFile /path/to/aws/key.pem
  ForwardAgent yes
  User ubuntu

Host vpc-00000000-jumpbox
  HostName 54.236.224.226
  IdentityFile /path/to/aws/key.pem
  ForwardAgent yes
  User ubuntu
```

This assumes that you only have one VPC in the ```us-west-1``` region
that you're trying to ssh into.  Internal DNS names aren't qualified
any further than that, so to support multiple VPC's you'd have to get
creative with subnets, for example ip-10-1 and ip-10-2...

Test this by typing `ssh ip-10-0-10-1.us-west-1.compute.internal`, 
(of course using a hostname exists in your environment.)  If things 
are configured correctly you will ssh to 10.0.10.1, jumping 
transparently via your basion host.

Getting this working in important because we'll be using Ansible
with the SSH transport and it will rely on this configuration
being in place in order to configure your servers.

jarv committed
93 94
### Tagging

95 96 97 98 99 100 101 102 103 104 105
Tagging is the bridge between the provisioning and configuration
phases.  The servers provisioned in your VPC will be stock Ubuntu
12.0.4 LTS servers.  The only difference between them with be the tags
that CloudFront has applied to them.  These tags will be used by Ansible
to map playbooks to the correct servers.  The application of the
appropriate playbook, will turn each stock host into an appropriately
configured service.

The *Group* tag is where the magic happens.  Every AWS EC2 instance
will have a *Group* tag that corresponds to a group of machines that
need to be deployed/targeted to as a group of servers.
jarv committed
106 107 108 109 110 111

**Example:**
* `Group`: `edxapp_stage`
* `Group`: `edxapp_prod`
* `Group`: `edxapp_some_other_environment`
 
Joe Blaylock committed
112 113
Additional tags can be added to AWS resources in the stack but they should not
be made necessary deployment or configuration.
jarv committed
114 115 116

## Ansible

Joe Blaylock committed
117 118
Ansible is a configuration management tool that edX is evaluating to replace
the puppet environment that is currently being used for edX servers.
jarv committed
119

jarv committed
120 121
http://ansible.cc/docs

Joe Blaylock committed
122 123
_Note: Because the directory structure changes in v1.2 we are using the dev
version instead of the official v1.1 release._
jarv committed
124 125


Joe Blaylock committed
126 127 128 129 130 131
* __Hosts__ -  The ec2.py inventory script generates an inventory file where
  hosts are assigned to groups. Individual hosts can be targeted by the "Name"
  tag or the instance ID. I don't think there will be a reason to set host
  specific variables.
* __Groups__ - A Group name is an identifier that corresponds to a group of
  roles plus an identifier for the environment.  Example: *edxapp_stage*,
132
  *edxapp_prod*, *xserver_stage*, etc.  For the purpose of targeting servers
Joe Blaylock committed
133
  for deployment groups are created automatically by the `ec2.py` inventory
134
  script since these group names will map to the _Group_ AWS tag. 
Joe Blaylock committed
135 136
* __Roles__  - A role will map to a single function/service that runs on
  server.
jarv committed
137

jarv committed
138
## Organization
jarv committed
139

140 141 142 143 144
### Secure vs. Insecure data

As a general policy we want to protect the following data:

* Usernames
145
* Public keys (keys are OK to be public, but can be used to figure out usernames)
146
* Hostnames
147
* Passwords, API keys
148

John Kern committed
149
The following yml files and examples serve as templates that should be overridden with your own
150 151
environment specific configuration:

John Jarvis committed
152 153
* vars in `secure_example/vars` 
* files in `secure_example/files` 
154

155
Directory structure for the secure repository:
156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175

```

ansible
├── files
├── keys
└── vars

```

The same directory structure, required yml files and files are 
in the secure_example dir:

```
secure_example/
├── files
├── keys
└── vars
```

John Kern committed
176
The default `secure_dir` is set in `group_vars/all` and can be overridden by
177 178 179
adding another file in group_vars that corresponds to a deploy group name.


jarv committed
180 181
The directory structure should follow Ansible best practices.

jarv committed
182 183
http://ansible.cc/docs/bestpractices.html

Joe Blaylock committed
184 185
* At the top level there are yml files for every group where a group name is an
  identifier that corresponds to a set of roles plus an environment.  
jarv committed
186
* The standard environments are _stage_ and _production_.
Joe Blaylock committed
187 188
* Additional environments can be named as well, below an example is given
  called _custom_.
jarv committed
189 190 191 192


### Variables

Joe Blaylock committed
193 194 195 196 197 198 199 200 201
* The ansible.cfg that is checked into the playbook directory has hash merging
  turned on, this allows us to to merge secure and custom data into the default
  variable definitions for every role.
* For example, `vars/lms_vars.yml` (variables needed for the lms role) sets the
  `env_config` which has keys that can be overridden by
  `vars/secure/edxapp_stage_vars.yml` for setting passwords and hostnames.  
* If needed, additional configuration can be layered, in the example
  `vars/secure/custom_vars.yml` changes some paramters that are set in
  `vars/secure/edxapp_stage_vars.yml`.
jarv committed
202

Joe Blaylock committed
203 204 205
__TODO__: _The secure/ directories are checked into the public repo for now as an
example, these will need to be moved to a private repo or maintained outside of
github._
jarv committed
206 207 208 209 210

### Users and Groups

There are two classes of users, admins and environment users.

Joe Blaylock committed
211 212 213 214
* The *admin_users* hash will be added to every server and will be put into a
  group that has admin bits.
* The *env_users* hash are the class of users that can be optionally included
  in one of the group-environment playbooks.
jarv committed
215 216


Joe Blaylock committed
217
Example users are in the `vars/secure` directory:
jarv committed
218

Joe Blaylock committed
219 220
* [*env_users* for staging environment](/vars/secure/edxapp_stage_users.yml)
* [*admin_users* will be realized on every server](/vars/secure/users.yml)
jarv committed
221

Joe Blaylock committed
222

223 224
```
cloudformation_templates  <-- official edX cloudformation templates
Jason Bau committed
225 226 227
    └── examples          <-- example templates
playbooks
 └──
Joe Blaylock committed
228 229 230
     edxapp_prod.yml      <-- example production environment playbook
     edxapp_stage.yml     <-- example stage environment playbook
     edxapp_custom.yml    <-- example custom environment playbook
231 232 233 234 235 236 237 238 239
    ├── files             <-- edX cloudformation templates
    │   └── examples      <-- example cloudformation templates
    ├── group_vars        <-- var files that correspond to ansible group names (mapped to AWS tags)
    ├── keys              <-- public keys
    ├── roles             <-- edX services
    │   ├── common        <-- tasks that are run for all roles
    │   │   └── tasks
    │   ├── lms
    │   │   ├── tasks     <-- tasks that are run to setup an LMS
240 241 242
    │   │   ├── templates
    │   │   └── vars      <-- main.yml in this directory is auto-loaded when the role is included
    │   │
243
    │   └── nginx
244
    │       ├── handlers 
245
    │       ├── tasks
246 247 248
    │       ├── vars
    │       └── templates 
    │   (etc)
249
    └── vars             <-- public variable definitions
Jason Bau committed
250
    └── secure_example   <-- secure variables (example)
251 252

```
jarv committed
253

jarv committed
254

jarv committed
255 256 257 258 259
### Installation

```
  mkvirtualenv ansible
  pip install -r ansible-requirements.txt
260
  util/sync_hooks.sh
jarv committed
261 262
```

jarv committed
263
### Launching example cloudformation stack - Working example
jarv committed
264

jarv committed
265
#### Provision the stack
jarv committed
266

267 268
**This assumes that you have workng ssh as described above**

jarv committed
269 270
  ```
  cd playbooks
Joe Blaylock committed
271
  ansible-playbook  -vvv cloudformation.yml -i inventory.ini  -e 'region=<aws_region> key=<key_name> name=<stack_name> group=<group_name>'
jarv committed
272
  ```
jarv committed
273
  
274
* _aws_region_: example: `us-east-1`. Which AWS EC2 region to build stack in.
Joe Blaylock committed
275 276 277 278
* _key_name_: example: `deploy`. SSH key name configured in AWS for the region
* _stack_name_: example: `EdxAppCustom`. Name of the stack, must not contain
  underscores or cloudformation will complain. Must be an unused name or
  otherwise the existing stack will update.
John Kern committed
279
* _group_name_: example: `edxapp_stage`. The group name should correspond to
Joe Blaylock committed
280 281 282
  one of the yml files in the `playbooks/`. Used for grouping hosts.

While this is running you see the cloudformation events in the AWS console as
Joe Blaylock committed
283
the stack is brought up.  Loads the `playbooks/cloudformation.yml` template
Joe Blaylock committed
284
which creates a single small EBS backed EC2 instance.  
jarv committed
285

Joe Blaylock committed
286 287 288
_Note: You should read the output from ansible and not necessarily trust the
'ok'; failures in cloudformation provisioning (for example, in creating the
security group), may not cause ansible-playbook to fail._
jarv committed
289

Joe Blaylock committed
290 291
See files/examples for
adding other components to the stack.
jarv committed
292

Joe Blaylock committed
293
##### If ansible-playbook gives you import errors
jarv committed
294

Joe Blaylock committed
295 296 297 298 299 300 301
Ansible really wants to call /usr/bin/python and if you have good virtualenv
hygeine, this may lead to ansible being unable to import critical libraries
like cloudfront. If you run into this problem, try exporting PYTHONPATH inside
your virtualenv and see if it runs better that way. E.g.:

  ```
  export PYTHONPATH=$VIRTUAL_ENV/lib/python2.7/site-packages/ 
Joe Blaylock committed
302
  ansible-playbook playbooks/cloudformation.yml -i playbooks/inventory.ini
Joe Blaylock committed
303 304 305 306
  ```

If that works fine, then you can add an export of PYTHONPATH to
`$VIRTUAL_ENV/bin/postactivate` so that you no longer have to think about it.
jarv committed
307
  
jarv committed
308
### Configure the stack
jarv committed
309

jarv committed
310 311 312
* Creates admin and env users
* Creates base directories
* Creates the lms json configuration files
jarv committed
313

e0d committed
314 315 316 317 318

Assuming that the edxapp_stage.yml playbook targets hosts in your vpc
for which there are entiries in your `.ssh/config`, do the 
following to run your playbook.

jarv committed
319
```
jarv committed
320
  cd playbooks
e0d committed
321
  ansible-playbook -v --user=ubuntu edxapp_stage.yml -i ./ec2.py -c ssh
jarv committed
322 323 324 325
```

*Note: this assumes the group used for the edx stack was "edxapp_stage"*