This is an automated email from the ASF dual-hosted git repository.

kezhenxu94 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/skywalking-terraform.git


The following commit(s) were added to refs/heads/main by this push:
     new bd31bcf  Add database provisioner, ALB and bastion host (#28)
bd31bcf is described below

commit bd31bcfb956a8ebd15770383ed33d6f259f805a1
Author: kezhenxu94 <[email protected]>
AuthorDate: Fri Sep 1 20:43:07 2023 +0800

    Add database provisioner, ALB and bastion host (#28)
    
    - 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
+}


Reply via email to