This is an automated email from the ASF dual-hosted git repository. kezhenxu94 pushed a commit to branch database in repository https://gitbox.apache.org/repos/asf/skywalking-terraform.git
commit 140de6e241662bff2c1623b196dc4a929d6bc1ef Author: kezhenxu94 <[email protected]> AuthorDate: Fri Sep 1 20:41:10 2023 +0800 Add database provisioner, ALB and bastion host - Add database provisioner, currently support H2 and rds-postgresql. - Add an AWS ALB so that we can access the SkyWalking UI via ALB, instead of doing port forward. - For security reason, add a bastion host and all operations to OAP and UI instances are done via bastion host as a proxy. - Simplify some command line options by adding them into files. - Generate Terraform docs for inputs, outputs, resources, etc. --- .gitignore | 1 + README.md | 58 +++++----- ansible/inventory/template/skywalking.yaml.tftpl | 22 +++- ansible/roles/skywalking/tasks/main.yml | 6 +- .../skywalking/templates/skywalking-oap.env.j2 | 14 +++ .../skywalking/templates/skywalking-oap.service.j2 | 3 +- .../skywalking/templates/skywalking-ui.env.j2 | 4 +- aws/README.md | 84 ++++++++++++++ aws/alb-main.tf | 70 ++++++++++++ aws/{key-pair-output.tf => alb-output.tf} | 5 +- .../skywalking-oap.service.j2 => aws/aws-main.tf | 23 ++-- aws/bastion-main.tf | 68 ++++++++++++ aws/{key-pair-output.tf => bastion-output.tf} | 5 +- aws/ec2-main.tf | 61 ++++------- aws/key-pair-output.tf | 3 +- aws/rds-postgresql-main.tf | 94 ++++++++++++++++ aws/rds-postgresql-output.tf | 43 ++++++++ aws/skywalking-oap-main.tf | 20 +++- aws/skywalking-oap-output.tf | 3 +- aws/skywalking-ui-main.tf | 34 +++--- aws/skywalking-ui-output.tf | 3 +- aws/variables.tf | 121 ++++++++++++++++++--- .../skywalking-oap.service.j2 => aws/vpc.tf | 31 +++--- 23 files changed, 633 insertions(+), 143 deletions(-) diff --git a/.gitignore b/.gitignore index ea6ebe3..29354fb 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,4 @@ aws/terraform.tfstate.backup ansible/local.var.yaml ansible/inventory !ansible/inventory/template +.terraform.tfstate.lock.info diff --git a/README.md b/README.md index 9e0d319..1fe6a67 100644 --- a/README.md +++ b/README.md @@ -42,16 +42,7 @@ terraform init The script is designed with modularity and reusability in mind. Various parameters like region, instance count, instance type, etc., are exposed as variables for easier customization. -#### Variables: - -| Variable Name | Description | Default Value | -|---------------------|------------------------------------------------------|-----------------------------| -| `oap_instance_count`| Number of SkyWalking OAP instances | `1` | -| `ui_instance_count` | Number of SkyWalking UI instances | `1` | -| `region` | AWS region where resources will be provisioned | `us-east-1` | -| `instance_type` | AWS instance type for SkyWalking OAP and UI | `t2.medium` | -| `public_key_path` | Path where the SSH key for instances will be stored | `~/.ssh` | -| `extra_tags` | Additional tags that can be applied to all resources | `{}` | +For the full configuration list, please refer to [the doc](/aws/README.md). To modify the default values, you can create a `terraform.tfvars` file in the same directory as your Terraform script: @@ -60,7 +51,6 @@ oap_instance_count = 2 ui_instance_count = 2 region = "us-west-1" instance_type = "t2.large" -public_key_path = "/path/to/your/desired/location" extra_tags = { "Environment" = "Production" } @@ -75,17 +65,24 @@ terraform plan terraform apply ``` +After all the resources are created, you can head to the +[Ansible part](#ansible) to start deploying SkyWalking. + ### 4. Accessing the Resources -Once the resources are created: +#### SSH into bastion host (Optional) -- **SkyWalking OAP and UI instances**: You can SSH into the instances using the generated key pair. The public IPs of these instances are stored in local files (`oap-server` and `ui-server` respectively) under the `ansible/inventory/` directory, relative to the module's path. +You don't usually need to SSH into the bastion host, but if you want to, you can +SSH into the bastion host with the command: -```bash -ssh -i /path/to/skywalking.pem ec2-user@<INSTANCE_PUBLIC_IP> +```shell +KEY_FILE=$(terraform output -raw ssh-user-key-file) +BASTION_IP=$(terraform output -json bastion_ips | jq -r '.[0]') + +ssh -i "$KEY_FILE" ec2-user@"$BASTION_IP" ``` -- **Security Groups**: Two security groups are created: +- **Security Attention**: two security rules are created for the bastion host: - `ssh-access`: Allows SSH access from any IP (`0.0.0.0/0`). **Please note** that this is potentially insecure and you should restrict the IP range wherever possible. - `public-egress-access`: Allows egress access to the internet for the instances. @@ -118,26 +115,18 @@ This guide provides steps on using Ansible to install Apache SkyWalking on AWS i ## Instructions -### 1. Change diroectory and set the SSH Key File Path +### 1. Change diroectory -Save the SSH key file path generated by Terraform to a variable for future use: - -``` +```shell cd ../ansible/ -SSH_KEY_FILE=$(terraform -chdir=../aws output -raw ssh-user-key-file) -echo $SSH_KEY_FILE ``` -**Expected Output**: - -You should see a file path similar to: `/Users/kezhenxu94/.ssh/skywalking.pem` - ### 2. Test Connectivity to the EC2 Instances Before installing SkyWalking, ensure that you can connect to the EC2 instances: ``` -ANSIBLE_HOST_KEY_CHECKING=False ansible -m ping all -u ec2-user --private-key "$SSH_KEY_FILE" +ansible -m ping all -u ec2-user ``` **Expected Output**: @@ -165,7 +154,7 @@ You should see output for each IP with a `SUCCESS` status: After confirming connectivity, proceed to install Apache SkyWalking using the Ansible playbook: ``` -ANSIBLE_HOST_KEY_CHECKING=False ansible-playbook -u ec2-user --private-key "$SSH_KEY_FILE" playbooks/install-skywalking.yml +ansible-playbook -u ec2-user playbooks/install-skywalking.yml -i inventory/skywalking.yaml ``` ### 4. Configurations @@ -196,3 +185,16 @@ skywalking_ui_environment: {} skywalking_oap_environment: {} ``` + +### 5. Accessing SkyWalking UI! + +After the installation is complete, you can go back to the aws folder and get +the ALB domain name address that can be used to access the SkyWalking UI: + +```shell +cd ../aws +terraform output -raw alb_dns_name +``` + +And you can open your browser and access the SkyWalking UI with the address. + diff --git a/ansible/inventory/template/skywalking.yaml.tftpl b/ansible/inventory/template/skywalking.yaml.tftpl index 59b2cbd..a979a64 100644 --- a/ansible/inventory/template/skywalking.yaml.tftpl +++ b/ansible/inventory/template/skywalking.yaml.tftpl @@ -16,7 +16,15 @@ # specific language governing permissions and limitations # under the License. # + +proxy: + ${bastion.public_ip}: + skywalking: + vars: + ansible_ssh_private_key_file: ${private_key_file} + ansible_ssh_user: ec2-user + ansible_ssh_common_args: '-o StrictHostKeyChecking=no -o ProxyCommand="ssh -i ${private_key_file} -o StrictHostKeyChecking=no -W %h:%p -q ec2-user@${bastion.public_ip}"' children: skywalking_oap: skywalking_ui: @@ -24,13 +32,19 @@ skywalking: skywalking_oap: hosts: %{ for oap in oap_instances ~} - ${oap.public_ip}: - private_ip: ${oap.private_ip} + ${oap.private_ip}: %{ endfor ~} + vars: + database: + type: ${database_type} + host: ${database_host} + port: ${database_port} + name: ${database_name} + user: ${database_user} + password: ${database_password} skywalking_ui: hosts: %{ for ui in ui_instances ~} - ${ui.public_ip}: - private_ip: ${ui.private_ip} + ${ui.private_ip}: %{ endfor ~} diff --git a/ansible/roles/skywalking/tasks/main.yml b/ansible/roles/skywalking/tasks/main.yml index b8c593b..60f1bac 100644 --- a/ansible/roles/skywalking/tasks/main.yml +++ b/ansible/roles/skywalking/tasks/main.yml @@ -52,7 +52,7 @@ src: skywalking-ui.env.j2 dest: /home/skywalking/webapp.env owner: skywalking - mode: "0660" + mode: "0600" when: inventory_hostname in groups['skywalking_ui'] - name: Generate environment file for OAP service @@ -60,7 +60,7 @@ src: skywalking-oap.env.j2 dest: /home/skywalking/oap.env owner: skywalking - mode: "0660" + mode: "0600" when: inventory_hostname in groups['skywalking_oap'] - name: Check hostgroup size @@ -69,7 +69,7 @@ oap_init_node: "{{ [groups['skywalking_oap'][0]] }}" - name: Run the OAPSericeInit script - command: "sudo -u skywalking /usr/local/skywalking/bin/oapServiceInit.sh" + command: "sudo -u skywalking -- sh -c 'set -a; source /home/skywalking/oap.env; set +a; /usr/local/skywalking/bin/oapServiceInit.sh'" when: inventory_hostname in oap_init_node - name: Generate systemd unit file for oap service diff --git a/ansible/roles/skywalking/templates/skywalking-oap.env.j2 b/ansible/roles/skywalking/templates/skywalking-oap.env.j2 index f11517b..981d0d4 100644 --- a/ansible/roles/skywalking/templates/skywalking-oap.env.j2 +++ b/ansible/roles/skywalking/templates/skywalking-oap.env.j2 @@ -16,6 +16,20 @@ # specific language governing permissions and limitations # under the License. # + +{% set database = hostvars[inventory_hostname]["database"] %} +{% set storage = database['type'] %} + +{% if storage and (storage | length) %} +SW_STORAGE={{ storage | regex_replace('^rds-', '')}} +{% endif %} + +{% if "postgresql" in storage %} +SW_JDBC_URL=jdbc:postgresql://{{ database["host"] }}:{{ database["port"] }}/{{ database["name"] }} +SW_DATA_SOURCE_USER={{ database['user'] }} +SW_DATA_SOURCE_PASSWORD={{ database['password'] }} +{% endif %} + {% for key, value in skywalking_oap_environment.items() %} {{ key }}="{{ value }}" {% endfor %} diff --git a/ansible/roles/skywalking/templates/skywalking-oap.service.j2 b/ansible/roles/skywalking/templates/skywalking-oap.service.j2 index aff39da..a6f6cf1 100644 --- a/ansible/roles/skywalking/templates/skywalking-oap.service.j2 +++ b/ansible/roles/skywalking/templates/skywalking-oap.service.j2 @@ -21,7 +21,8 @@ After=network.target Type=simple User=skywalking Group=skywalking -ExecStart=/usr/local/skywalking/bin/oapService.sh +EnvironmentFile=/home/skywalking/oap.env +ExecStart=/usr/local/skywalking/bin/oapServiceNoInit.sh TimeoutSec=300 KillMode=process ExecReload=/bin/kill -HUP $MAINPID diff --git a/ansible/roles/skywalking/templates/skywalking-ui.env.j2 b/ansible/roles/skywalking/templates/skywalking-ui.env.j2 index 0ca001f..017defd 100644 --- a/ansible/roles/skywalking/templates/skywalking-ui.env.j2 +++ b/ansible/roles/skywalking/templates/skywalking-ui.env.j2 @@ -20,6 +20,6 @@ {{ key }}="{{ value }}" {% endfor %} -SW_OAP_ADDRESS="{% for host in groups['skywalking_oap'] %}http://{{ hostvars[host].private_ip }}:{{ skywalking_ui_environment['SW_CORE_GRPC_PORT'] | default ('12800') }}{% if not loop.last %},{% endif %}{% endfor %}" -SW_ZIPKIN_ADDRESS="{% for host in groups['skywalking_oap'] %}http://{{ hostvars[host].private_ip }}:{{ skywalking_ui_environment['SW_QUERY_ZIPKIN_REST_PORT'] | default ('9412') }}{% if not loop.last %},{% endif %}{% endfor %}" +SW_OAP_ADDRESS="{% for host in groups['skywalking_oap'] %}http://{{ hostvars[host].inventory_hostname }}:{{ skywalking_ui_environment['SW_CORE_GRPC_PORT'] | default ('12800') }}{% if not loop.last %},{% endif %}{% endfor %}" +SW_ZIPKIN_ADDRESS="{% for host in groups['skywalking_oap'] %}http://{{ hostvars[host].inventory_hostname }}:{{ skywalking_ui_environment['SW_QUERY_ZIPKIN_REST_PORT'] | default ('9412') }}{% if not loop.last %},{% endif %}{% endfor %}" diff --git a/aws/README.md b/aws/README.md new file mode 100644 index 0000000..3d3f3f3 --- /dev/null +++ b/aws/README.md @@ -0,0 +1,84 @@ +<!-- BEGIN_TF_DOCS --> +## Requirements + +No requirements. + +## Providers + +| Name | Version | +|------|---------| +| <a name="provider_aws"></a> [aws](#provider\_aws) | 5.10.0 | +| <a name="provider_local"></a> [local](#provider\_local) | 2.4.0 | +| <a name="provider_random"></a> [random](#provider\_random) | 3.5.1 | +| <a name="provider_tls"></a> [tls](#provider\_tls) | 4.0.4 | + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| <a name="module_alb"></a> [alb](#module\_alb) | terraform-aws-modules/alb/aws | ~> 8.0 | +| <a name="module_rds"></a> [rds](#module\_rds) | terraform-aws-modules/rds/aws | ~> 5.0 | +| <a name="module_vpc"></a> [vpc](#module\_vpc) | terraform-aws-modules/vpc/aws | ~> 5.0 | + +## Resources + +| Name | Type | +|------|------| +| [aws_instance.bastion](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/instance) | resource | +| [aws_instance.skywalking-oap](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/instance) | resource | +| [aws_instance.skywalking-ui](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/instance) | resource | +| [aws_key_pair.ssh-user](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/key_pair) | resource | +| [aws_security_group.allow_apps](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource | +| [aws_security_group.bastion](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource | +| [aws_security_group.public-egress-access](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource | +| [aws_security_group.skywalking-oap](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource | +| [aws_security_group.skywalking-ui](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource | +| [local_file.inventories](https://registry.terraform.io/providers/hashicorp/local/latest/docs/resources/file) | resource | +| [local_file.ssh-user](https://registry.terraform.io/providers/hashicorp/local/latest/docs/resources/file) | resource | +| [random_password.rds_password](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/password) | resource | +| [tls_private_key.ssh-user](https://registry.terraform.io/providers/hashicorp/tls/latest/docs/resources/private_key) | resource | +| [aws_ami.amazon-linux](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ami) | data source | +| [aws_availability_zones.available](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/availability_zones) | data source | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| <a name="input_access_key"></a> [access\_key](#input\_access\_key) | Access key of the AWS account, if you have configured AWS CLI, you can leave it empty. | `string` | `""` | no | +| <a name="input_bastion_enabled"></a> [bastion\_enabled](#input\_bastion\_enabled) | Enable bastion host, if you want to access the instances via SSH, you must enable it. | `bool` | `true` | no | +| <a name="input_bastion_instance_type"></a> [bastion\_instance\_type](#input\_bastion\_instance\_type) | CPU, memory, storage and networking capacity for bastion host | `string` | `"t2.micro"` | no | +| <a name="input_cidr"></a> [cidr](#input\_cidr) | CIDR for database tier | `string` | `"11.0.0.0/16"` | no | +| <a name="input_cluster_name"></a> [cluster\_name](#input\_cluster\_name) | Name of the cluster | `string` | `"skywalking-cluster"` | no | +| <a name="input_database_subnets"></a> [database\_subnets](#input\_database\_subnets) | CIDR used for database subnets | `set(string)` | <pre>[<br> "11.0.104.0/24",<br> "11.0.105.0/24",<br> "11.0.106.0/24"<br>]</pre> | no | +| <a name="input_db_instance_class"></a> [db\_instance\_class](#input\_db\_instance\_class) | Instance class for the database | `string` | `"db.t3.medium"` | no | +| <a name="input_db_max_storage_size"></a> [db\_max\_storage\_size](#input\_db\_max\_storage\_size) | Maximum storage size for the database, in GB | `number` | `100` | no | +| <a name="input_db_name"></a> [db\_name](#input\_db\_name) | Name of the database | `string` | `"skywalking"` | no | +| <a name="input_db_password"></a> [db\_password](#input\_db\_password) | Password for the database, if not set, a random password will be generated. | `string` | `null` | no | +| <a name="input_db_storage_size"></a> [db\_storage\_size](#input\_db\_storage\_size) | Storage size for the database, in GB | `number` | `5` | no | +| <a name="input_db_username"></a> [db\_username](#input\_db\_username) | Username for the database | `string` | `"skywalking"` | no | +| <a name="input_extra_tags"></a> [extra\_tags](#input\_extra\_tags) | Additional tags to be added to all resources | `map(string)` | `{}` | no | +| <a name="input_instance_type"></a> [instance\_type](#input\_instance\_type) | CPU, memory, storage and networking capacity for OAP and UI instances | `string` | `"t2.medium"` | no | +| <a name="input_oap_instance_count"></a> [oap\_instance\_count](#input\_oap\_instance\_count) | Number of OAP instances, if you want to use H2 storage, you must set it to 1. | `number` | `1` | no | +| <a name="input_private_subnets"></a> [private\_subnets](#input\_private\_subnets) | CIDR used for private subnets | `set(string)` | <pre>[<br> "11.0.1.0/24",<br> "11.0.2.0/24",<br> "11.0.3.0/24"<br>]</pre> | no | +| <a name="input_public_key_path"></a> [public\_key\_path](#input\_public\_key\_path) | Path to store the key file for SSH access to the instances. | `string` | `"~/.ssh"` | no | +| <a name="input_public_subnets"></a> [public\_subnets](#input\_public\_subnets) | CIDR used for public subnets | `set(string)` | <pre>[<br> "11.0.101.0/24",<br> "11.0.102.0/24",<br> "11.0.103.0/24"<br>]</pre> | no | +| <a name="input_region"></a> [region](#input\_region) | Physical location for clustered data centers. | `string` | `"us-east-1"` | no | +| <a name="input_secret_key"></a> [secret\_key](#input\_secret\_key) | Secret key of the AWS account, if you have configured AWS CLI, you can leave it empty. | `string` | `""` | no | +| <a name="input_storage"></a> [storage](#input\_storage) | Storage type for SkyWalking OAP, can be 'h2', or 'rds-postgresql' | `string` | `"rds-postgresql"` | no | +| <a name="input_ui_instance_count"></a> [ui\_instance\_count](#input\_ui\_instance\_count) | Number of UI instances | `number` | `1` | no | + +## Outputs + +| Name | Description | +|------|-------------| +| <a name="output_alb_dns_name"></a> [alb\_dns\_name](#output\_alb\_dns\_name) | The domain name of the ALB that can be used to access SkyWalking UI. | +| <a name="output_bastion_ips"></a> [bastion\_ips](#output\_bastion\_ips) | The public IP that can be used to SSH into the bastion host. | +| <a name="output_database_address"></a> [database\_address](#output\_database\_address) | The database address | +| <a name="output_database_name"></a> [database\_name](#output\_database\_name) | The database name | +| <a name="output_database_password"></a> [database\_password](#output\_database\_password) | The database password | +| <a name="output_database_port"></a> [database\_port](#output\_database\_port) | The database port | +| <a name="output_database_username"></a> [database\_username](#output\_database\_username) | The database username | +| <a name="output_skywalking_oap_ips"></a> [skywalking\_oap\_ips](#output\_skywalking\_oap\_ips) | The private IPs of the OAP instances | +| <a name="output_skywalking_ui_ips"></a> [skywalking\_ui\_ips](#output\_skywalking\_ui\_ips) | The IPs of the SkyWalking UI instances | +| <a name="output_ssh-user-key-file"></a> [ssh-user-key-file](#output\_ssh-user-key-file) | The SSH key file that can be used to connect to the bastion instance. | +<!-- END_TF_DOCS --> \ No newline at end of file diff --git a/aws/alb-main.tf b/aws/alb-main.tf new file mode 100644 index 0000000..a1cfc28 --- /dev/null +++ b/aws/alb-main.tf @@ -0,0 +1,70 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +module "alb" { + source = "terraform-aws-modules/alb/aws" + version = "~> 8.0" + + name = var.cluster_name + + load_balancer_type = "application" + + vpc_id = module.vpc.vpc_id + subnets = module.vpc.public_subnets + security_groups = [module.vpc.default_security_group_id] + + security_group_rules = { + ingress_all_http = { + type = "ingress" + from_port = 80 + to_port = 80 + protocol = "tcp" + description = "Allow HTTP traffic" + cidr_blocks = ["0.0.0.0/0"] + } + egress_all = { + type = "egress" + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } + } + + target_groups = [ + { + name_prefix = substr(var.cluster_name, 0, 6) + backend_protocol = "HTTP" + backend_port = 8080 + target_type = "instance" + targets = [ + for i, ui in aws_instance.skywalking-ui : { + target_id = ui.id + port = 8080 + } + ] + } + ] + + http_tcp_listeners = [ + { + port = 80 + protocol = "HTTP" + target_group_index = 0 + } + ] + + tags = var.extra_tags +} diff --git a/aws/key-pair-output.tf b/aws/alb-output.tf similarity index 84% copy from aws/key-pair-output.tf copy to aws/alb-output.tf index 21ea191..2b7561b 100644 --- a/aws/key-pair-output.tf +++ b/aws/alb-output.tf @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. -output "ssh-user-key-file" { - value = local_file.ssh-user.filename +output "alb_dns_name" { + value = module.alb.lb_dns_name + description = "The domain name of the ALB that can be used to access SkyWalking UI." } diff --git a/ansible/roles/skywalking/templates/skywalking-oap.service.j2 b/aws/aws-main.tf similarity index 72% copy from ansible/roles/skywalking/templates/skywalking-oap.service.j2 copy to aws/aws-main.tf index aff39da..15aad31 100644 --- a/ansible/roles/skywalking/templates/skywalking-oap.service.j2 +++ b/aws/aws-main.tf @@ -13,19 +13,12 @@ # See the License for the specific language governing permissions and # limitations under the License. -[Unit] -Description=Apache SkyWalking OAP Service -After=network.target +provider "aws" { + region = var.region + access_key = var.access_key + secret_key = var.secret_key +} -[Service] -Type=simple -User=skywalking -Group=skywalking -ExecStart=/usr/local/skywalking/bin/oapService.sh -TimeoutSec=300 -KillMode=process -ExecReload=/bin/kill -HUP $MAINPID -Restart=on-failure - -[Install] -WantedBy=multi-user.target +data "aws_availability_zones" "available" { + state = "available" +} diff --git a/aws/bastion-main.tf b/aws/bastion-main.tf new file mode 100644 index 0000000..6ddb9df --- /dev/null +++ b/aws/bastion-main.tf @@ -0,0 +1,68 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +resource "aws_instance" "bastion" { + count = var.bastion_enabled ? 1 : 0 + ami = data.aws_ami.amazon-linux.id + instance_type = var.bastion_instance_type + key_name = aws_key_pair.ssh-user.id + subnet_id = element(module.vpc.public_subnets, 0) + associate_public_ip_address = true + + vpc_security_group_ids = [ + aws_security_group.bastion.id, + aws_security_group.public-egress-access.id + ] + tags = merge( + { + Name = "Bastion Host" + Description = "Bastion host for SSH access" + }, + var.extra_tags + ) + + connection { + host = self.public_ip + user = "ec2-user" + type = "ssh" + private_key = tls_private_key.ssh-user.private_key_pem + } + + provisioner "file" { + content = tls_private_key.ssh-user.private_key_pem + destination = "/home/ec2-user/.ssh/id_rsa" + + } + + provisioner "remote-exec" { + inline = ["chmod og-rwx /home/ec2-user/.ssh/id_rsa"] + } +} + +resource "aws_security_group" "bastion" { + name = "bastion" + description = "Security group for bastion" + vpc_id = module.vpc.vpc_id + + ingress { + description = "SSH access from the Internet" + from_port = 22 + to_port = 22 + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] + } + + tags = var.extra_tags +} diff --git a/aws/key-pair-output.tf b/aws/bastion-output.tf similarity index 83% copy from aws/key-pair-output.tf copy to aws/bastion-output.tf index 21ea191..03fc206 100644 --- a/aws/key-pair-output.tf +++ b/aws/bastion-output.tf @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. -output "ssh-user-key-file" { - value = local_file.ssh-user.filename +output "bastion_ips" { + value = aws_instance.bastion.*.public_ip + description = "The public IP that can be used to SSH into the bastion host." } diff --git a/aws/ec2-main.tf b/aws/ec2-main.tf index 1f2ea25..ce36306 100644 --- a/aws/ec2-main.tf +++ b/aws/ec2-main.tf @@ -13,47 +13,20 @@ # See the License for the specific language governing permissions and # limitations under the License. -provider "aws" { - region = var.region - access_key = var.access_key - secret_key = var.secret_key -} - -resource "aws_security_group" "ssh-access" { - name = "ssh-access" - description = "Allow SSH access from the Internet" - ingress = [ - { - from_port = 22 - to_port = 22 - protocol = "tcp" - cidr_blocks = ["0.0.0.0/0"] - description = "Allow SSH access from the Internet" - ipv6_cidr_blocks = [] - prefix_list_ids = [] - security_groups = [] - self = false - } - ] - tags = var.extra_tags -} - resource "aws_security_group" "public-egress-access" { name = "public-egress-access" description = "Allow access to the Internet" - egress = [ - { - from_port = 0 - to_port = 0 - protocol = -1 - cidr_blocks = ["0.0.0.0/0"] - description = "Allow access to the Internet" - ipv6_cidr_blocks = [] - prefix_list_ids = [] - security_groups = [] - self = false - } - ] + vpc_id = module.vpc.vpc_id + + egress { + from_port = 0 + to_port = 0 + protocol = -1 + cidr_blocks = ["0.0.0.0/0"] + description = "Allow access to the Internet" + security_groups = [] + } + tags = var.extra_tags } @@ -61,7 +34,15 @@ resource "local_file" "inventories" { filename = "${path.module}/../ansible/inventory/skywalking.yaml" file_permission = "0600" content = templatefile("${path.module}/../ansible/inventory/template/skywalking.yaml.tftpl", { - oap_instances = aws_instance.skywalking-oap - ui_instances = aws_instance.skywalking-ui + bastion = aws_instance.bastion[0] + oap_instances = aws_instance.skywalking-oap + ui_instances = aws_instance.skywalking-ui + private_key_file = local_file.ssh-user.filename + database_type = var.storage + database_host = var.storage == "rds-postgresql" ? module.rds[0].db_instance_address : "" + database_port = var.storage == "rds-postgresql" ? module.rds[0].db_instance_port : "" + database_user = var.storage == "rds-postgresql" ? module.rds[0].db_instance_username : "" + database_name = var.storage == "rds-postgresql" ? module.rds[0].db_instance_name : "" + database_password = var.storage == "rds-postgresql" ? local.database_password : "" }) } diff --git a/aws/key-pair-output.tf b/aws/key-pair-output.tf index 21ea191..a1a4271 100644 --- a/aws/key-pair-output.tf +++ b/aws/key-pair-output.tf @@ -16,5 +16,6 @@ # under the License. output "ssh-user-key-file" { - value = local_file.ssh-user.filename + value = local_file.ssh-user.filename + description = "The SSH key file that can be used to connect to the bastion instance." } diff --git a/aws/rds-postgresql-main.tf b/aws/rds-postgresql-main.tf new file mode 100644 index 0000000..58f7958 --- /dev/null +++ b/aws/rds-postgresql-main.tf @@ -0,0 +1,94 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +resource "random_password" "rds_password" { + length = 16 + special = false +} + +locals { + database_password = var.db_password != null ? var.db_password : random_password.rds_password.result +} + +module "rds" { + source = "terraform-aws-modules/rds/aws" + version = "~> 5.0" + + count = var.storage == "rds-postgresql" ? 1 : 0 + + identifier = var.cluster_name + + allocated_storage = var.db_storage_size + max_allocated_storage = var.db_max_storage_size + + db_name = var.db_name + username = var.db_username + password = local.database_password + create_random_password = false + port = "5432" + + create_db_subnet_group = true + iam_database_authentication_enabled = true + skip_final_snapshot = true + + vpc_security_group_ids = [module.vpc.default_security_group_id, aws_security_group.allow_apps.id] + + multi_az = "false" + + maintenance_window = "Wed:00:00-Wed:03:00" + backup_window = "03:00-06:00" + backup_retention_period = "35" + + monitoring_role_name = "RDSMonitoringRole" + create_monitoring_role = false + + subnet_ids = module.vpc.database_subnets + + engine = "postgres" + engine_version = "15" + family = "postgres15" + major_engine_version = "15.3" + instance_class = var.db_instance_class + create_db_option_group = "false" + + parameters = [ + { + name = "client_encoding" + value = "utf8" + } + ] +} + +resource "aws_security_group" "allow_apps" { + name = "allow_apps" + description = "Allow apps inbound traffic and database outbound traffic" + vpc_id = module.vpc.vpc_id + + ingress { + from_port = 5432 + to_port = 5432 + protocol = "tcp" + security_groups = [aws_security_group.skywalking-oap.id] + } + + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } +} diff --git a/aws/rds-postgresql-output.tf b/aws/rds-postgresql-output.tf new file mode 100644 index 0000000..c3cb1fb --- /dev/null +++ b/aws/rds-postgresql-output.tf @@ -0,0 +1,43 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +output "database_address" { + value = var.storage == "rds-postgresql" ? module.rds[0].db_instance_address : "" + description = "The database address" +} + +output "database_port" { + value = var.storage == "rds-postgresql" ? module.rds[0].db_instance_port : "" + description = "The database port" +} + +output "database_name" { + value = var.storage == "rds-postgresql" ? module.rds[0].db_instance_name : "" + description = "The database name" +} + +output "database_username" { + value = var.storage == "rds-postgresql" ? module.rds[0].db_instance_username : "" + sensitive = true + description = "The database username" +} + +output "database_password" { + value = var.storage == "rds-postgresql" ? module.rds[0].db_instance_password : "" + sensitive = true + description = "The database password" +} diff --git a/aws/skywalking-oap-main.tf b/aws/skywalking-oap-main.tf index 17c1b3a..3d25f06 100644 --- a/aws/skywalking-oap-main.tf +++ b/aws/skywalking-oap-main.tf @@ -18,9 +18,10 @@ resource "aws_instance" "skywalking-oap" { ami = data.aws_ami.amazon-linux.id instance_type = var.instance_type key_name = aws_key_pair.ssh-user.id + subnet_id = element(module.vpc.private_subnets, 0) + vpc_security_group_ids = [ aws_security_group.skywalking-oap.id, - aws_security_group.ssh-access.id, aws_security_group.public-egress-access.id ] tags = merge( @@ -30,11 +31,20 @@ resource "aws_instance" "skywalking-oap" { }, var.extra_tags ) + + lifecycle { + precondition { + condition = !(var.oap_instance_count > 1 && var.storage == "h2") + error_message = "OAP instance count must be 1 if storage is h2" + } + } } resource "aws_security_group" "skywalking-oap" { name = "skywalking-oap" description = "Security group for SkyWalking OAP" + vpc_id = module.vpc.vpc_id + ingress { from_port = 12800 to_port = 12800 @@ -49,6 +59,14 @@ resource "aws_security_group" "skywalking-oap" { security_groups = [aws_security_group.skywalking-ui.id] description = "Allow incoming HTTP connections from SkyWalking UI" } + ingress { + from_port = 22 + to_port = 22 + protocol = "tcp" + description = "Allow SSH access from the bastion" + security_groups = [aws_security_group.bastion.id] + } + tags = var.extra_tags } diff --git a/aws/skywalking-oap-output.tf b/aws/skywalking-oap-output.tf index 5508145..d785e1a 100644 --- a/aws/skywalking-oap-output.tf +++ b/aws/skywalking-oap-output.tf @@ -16,6 +16,7 @@ # under the License. output "skywalking_oap_ips" { - value = ["${aws_instance.skywalking-oap.*.public_ip}"] + value = ["${aws_instance.skywalking-oap.*.private_ip}"] + description = "The private IPs of the OAP instances" } diff --git a/aws/skywalking-ui-main.tf b/aws/skywalking-ui-main.tf index 8644fe0..e367159 100644 --- a/aws/skywalking-ui-main.tf +++ b/aws/skywalking-ui-main.tf @@ -18,9 +18,10 @@ resource "aws_instance" "skywalking-ui" { ami = data.aws_ami.amazon-linux.id instance_type = var.instance_type key_name = aws_key_pair.ssh-user.id + subnet_id = element(module.vpc.private_subnets, 0) + vpc_security_group_ids = [ aws_security_group.skywalking-ui.id, - aws_security_group.ssh-access.id, aws_security_group.public-egress-access.id ] tags = merge( @@ -35,19 +36,24 @@ resource "aws_instance" "skywalking-ui" { resource "aws_security_group" "skywalking-ui" { name = "skywalking-ui" description = "Security group for SkyWalking UI" - ingress = [ - { - from_port = 8080 - to_port = 8080 - protocol = "tcp" - cidr_blocks = ["0.0.0.0/0"] - description = "Allow access from Intenet to SkyWalking UI" - ipv6_cidr_blocks = [] - prefix_list_ids = [] - security_groups = [] - self = false - } - ] + vpc_id = module.vpc.vpc_id + + ingress { + from_port = 8080 + to_port = 8080 + protocol = "tcp" + description = "Allow access from ALB to SkyWalking UI" + security_groups = [module.alb.security_group_id] + } + + ingress { + from_port = 22 + to_port = 22 + protocol = "tcp" + description = "Allow SSH access from the bastion" + security_groups = [aws_security_group.bastion.id] + } + tags = var.extra_tags } diff --git a/aws/skywalking-ui-output.tf b/aws/skywalking-ui-output.tf index 3b09582..89abac4 100644 --- a/aws/skywalking-ui-output.tf +++ b/aws/skywalking-ui-output.tf @@ -16,5 +16,6 @@ # under the License. output "skywalking_ui_ips" { - value = ["${aws_instance.skywalking-ui.*.public_ip}"] + value = ["${aws_instance.skywalking-ui.*.private_ip}"] + description = "The IPs of the SkyWalking UI instances" } diff --git a/aws/variables.tf b/aws/variables.tf index 4ed59fb..dfdf1d6 100644 --- a/aws/variables.tf +++ b/aws/variables.tf @@ -13,16 +13,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -variable "oap_instance_count" { - type = number - default = 1 -} - -variable "ui_instance_count" { - type = number - default = 1 -} - variable "region" { type = string description = "Physical location for clustered data centers." @@ -31,25 +21,55 @@ variable "region" { variable "access_key" { type = string - description = "Access key of the AWS account" + description = "Access key of the AWS account, if you have configured AWS CLI, you can leave it empty." default = "" } variable "secret_key" { type = string - description = "Secret key of the AWS account" + description = "Secret key of the AWS account, if you have configured AWS CLI, you can leave it empty." default = "" } +variable "cluster_name" { + type = string + description = "Name of the cluster" + default = "skywalking-cluster" +} + +variable "oap_instance_count" { + type = number + description = "Number of OAP instances, if you want to use H2 storage, you must set it to 1." + default = 1 +} + +variable "ui_instance_count" { + type = number + description = "Number of UI instances" + default = 1 +} + +variable "bastion_enabled" { + type = bool + description = "Enable bastion host, if you want to access the instances via SSH, you must enable it." + default = true +} + +variable "bastion_instance_type" { + type = string + description = "CPU, memory, storage and networking capacity for bastion host" + default = "t2.micro" +} + variable "instance_type" { type = string - description = "CPU, memory, storage and networking capacity" + description = "CPU, memory, storage and networking capacity for OAP and UI instances" default = "t2.medium" } variable "public_key_path" { type = string - description = "Path to store the key file for SSH access to the instances" + description = "Path to store the key file for SSH access to the instances." default = "~/.ssh" } @@ -59,3 +79,76 @@ variable "extra_tags" { default = {} } +## VPC +variable "cidr" { + type = string + description = "CIDR for database tier" + default = "11.0.0.0/16" +} + +variable "private_subnets" { + type = set(string) + description = "CIDR used for private subnets" + default = ["11.0.1.0/24", "11.0.2.0/24", "11.0.3.0/24"] +} + +variable "public_subnets" { + type = set(string) + description = "CIDR used for public subnets" + default = ["11.0.101.0/24", "11.0.102.0/24", "11.0.103.0/24"] +} + +variable "database_subnets" { + type = set(string) + description = "CIDR used for database subnets" + default = ["11.0.104.0/24", "11.0.105.0/24", "11.0.106.0/24"] +} + +## Storage +variable "storage" { + type = string + description = "Storage type for SkyWalking OAP, can be 'h2', or 'rds-postgresql'" + default = "rds-postgresql" + + validation { + condition = contains(["h2", "rds-postgresql"], var.storage) + error_message = "Allowed values for storage are \"h2\", \"rds-postgresql\"." + } +} + +variable "db_name" { + type = string + description = "Name of the database" + default = "skywalking" +} + +variable "db_username" { + type = string + description = "Username for the database" + default = "skywalking" +} + +variable "db_password" { + type = string + description = "Password for the database, if not set, a random password will be generated." + default = null +} + +variable "db_storage_size" { + type = number + description = "Storage size for the database, in GB" + default = 5 +} + +variable "db_max_storage_size" { + type = number + description = "Maximum storage size for the database, in GB" + default = 100 +} + +variable "db_instance_class" { + type = string + description = "Instance class for the database" + default = "db.t3.medium" +} + diff --git a/ansible/roles/skywalking/templates/skywalking-oap.service.j2 b/aws/vpc.tf similarity index 63% copy from ansible/roles/skywalking/templates/skywalking-oap.service.j2 copy to aws/vpc.tf index aff39da..63f8ad9 100644 --- a/ansible/roles/skywalking/templates/skywalking-oap.service.j2 +++ b/aws/vpc.tf @@ -13,19 +13,22 @@ # See the License for the specific language governing permissions and # limitations under the License. -[Unit] -Description=Apache SkyWalking OAP Service -After=network.target +module "vpc" { + source = "terraform-aws-modules/vpc/aws" + version = "~> 5.0" -[Service] -Type=simple -User=skywalking -Group=skywalking -ExecStart=/usr/local/skywalking/bin/oapService.sh -TimeoutSec=300 -KillMode=process -ExecReload=/bin/kill -HUP $MAINPID -Restart=on-failure + name = var.cluster_name + cidr = var.cidr -[Install] -WantedBy=multi-user.target + azs = data.aws_availability_zones.available.names + + private_subnets = var.private_subnets + public_subnets = var.public_subnets + database_subnets = var.database_subnets + + enable_nat_gateway = true + enable_vpn_gateway = false + single_nat_gateway = true + enable_dns_hostnames = true + enable_dns_support = true +}
