Run a HOP application on AWS ============================ Introduction ------------ In this tutorial you will bootstrap a basic HOP project with the following features: * `Duct Framework`_ based backend. * `Reactive`_ ClojureScript front-end. * Infrastructure provisioning in the Amazon Web Services deployment target. * CI/CD integration with `GitHub Actions`_. .. _Reactive: https://github.com/reagent-project/reagent .. _Duct Framework: https://github.com/duct-framework/duct .. _Amazon Web Services: https://aws.amazon.com/ .. _GitHub Actions: https://docs.github.com/en/actions .. note:: The HOP Command Line Interface tool (HOP CLI, from here on) provides more bootstrapping features, but this tutorial will focus in a bare minimum installation. Prerequisites ------------- Having the HOP CLI installed, as explained in the :doc:`/get-started/installation/main` tutorial, is enough for bootstrapping a new HOP project. But in order to run the generated project locally on your machine, you will need some additional tools: * `Docker `_ * `Docker Compose `_ * `AWS Vault `_ Apart from the software requirements, you will also need an Amazon Web Services account with administrator credentials, in order to create the infrastructure needed. Install Docker ++++++++++++++ As of this writing, HOP projects require Docker version 18.09 or later. You can check if you have Docker already installed, and that it is a supported version, by running the following command in a terminal: .. code-block:: console $ docker --version Docker version 18.09.1, build 4c52b90 If you do not have Docker installed, or if it is an older version than the ones supported, you can install a supported version on your operating system of choice, by following the instructions in the `Docker installation documentation`_. .. _Docker installation documentation: https://docs.docker.com/engine/install/ Once you have installed (or upgraded) Docker, you can check that it is correctly installed by running the following command: .. code-block:: console $ docker run --rm hello-world Hello from Docker! This message shows that your installation appears to be working correctly. [... more text appears here ...] For more examples and ideas, visit: https://docs.docker.com/get-started/ .. note:: If you get an error about not having permission to connect to the Docker daemon socket, check the `Docker post-installation steps documentation`_: .. _Docker post-installation steps documentation: https://docs.docker.com/engine/install/linux-postinstall/ You can also check that you have a supported version by re-running the command you used before: .. code-block:: console $ docker --version Docker version 18.09.1, build 4c52b90 Install Docker Compose ++++++++++++++++++++++ As of this writing, HOP projects require Docker Compose version 1.27.0 or later. You can check if you have Docker Compose already installed, with a supported version, by running the following command: .. code-block:: console $ docker-compose --version docker-compose version 1.27.0, build 980ec85b If you do not have Docker Compose installed, or if it is an older version than the ones supported, you can install a supported version on your operating system of choice, by following the instructions in the `Docker Compose installation documentation`_. .. _`Docker Compose installation documentation`: https://docs.docker.com/compose/install/ Once you have installed (or upgraded) Docker Compose, you can check that it is correctly installed, and using a supported version, by running the following command in a terminal: .. code-block:: console $ docker-compose --version docker-compose version 1.27.0, build 980ec85b Install AWS Vault +++++++++++++++++ As of this writing, no specific version of AWS Vault is required, but the latest available stable version is recommended. To install it in your operating system of choice, please refer to the `AWS Vault documentation`_. .. _AWS Vault documentation: https://github.com/99designs/aws-vault#installing .. note:: If you install AWS Vault by downloading an executable binary from GitHub Releases, do not forget to rename the downloaded file to ``aws-vault``. Also, if your operating system uses permissions to decide whether a file can be executed or not (e.g., Linux or macOS), make sure you set the executable permission to the ``aws-vault`` file. Once you have installed AWS Vault, you can check that it is installed correctly by running the following command: .. code-block:: console $ aws-vault --version v6.6.1 Prepare AWS credentials ----------------------- The HOP CLI will automatically provision the infrastructure needed to run the HOP project using `AWS Cloudformation`_. In order to do that, you will need an existing AWS account (an IAM User) with administrator access. .. warning:: **Do not** use the AWS root account user! `Create a new IAM User`_ and give it administrator access instead. That IAM User (the administrator user, from here on) will need security credentials in the form of an Access Key. If the administrator user does not have an Access Key yet, you can `create an Access Key`_ from the AWS Console. .. _AWS Cloudformation: https://aws.amazon.com/cloudformation/ .. _Create a new IAM User: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_users_create.html .. _create an Access Key: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html#Using_CreateAccessKey Although it is not mandatory, we strongly recommend storing the administrator user credentials using AWS Vault [#AdminAwsVaultCreds]_. Once you have the credentials, you can store them in AWS Vault using the following command. The command assumes that you will configure the "aws-vault prefix" configuration setting as indicated below. If you configure a different prefix, make sure to adjust this command to use the right one: .. code-block:: console $ aws-vault add --add-config hop/hop-tutorial-admin The tool will ask you for the administrator user AWS Access Key Id, and its corresponding AWS Secret Access Key. Once you enter them, the setup will be done. .. note:: If you get an error saying: ``aws-vault: error: Specified keyring backend not available, try --help`` you are probably running AWS Vault in a system with no graphical environment. In that case, for this tutorial, you can use the ``AWS_VAULT_BACKEND`` environment variable and specify the ``file`` backend type. That backend will ask you for a passphrase and store the credentials in an encrypted file on disk. See `aws-vault Backends documentation`_ for additional details and, as suggested, run: .. code-block:: console $ aws-vault add --help for additional available options. .. _`aws-vault Backends documentation`: https://github.com/99designs/aws-vault/blob/master/USAGE.md#backends Configure the project settings ------------------------------ The HOP CLI allows the user to configure certain characteristics of the project to be generated and provisioned in AWS. That configuration is specified using a settings file. This tutorial uses the web-based Settings Editor for creating and editing that file. If you would like to edit the settings manually please refer to :doc:`/get-started/run-hop-application-on-aws/appendix/edit-settings-file-manually`. First launch the HOP CLI Settings Editor by running the following command: .. code-block:: console $ hop bootstrap open-settings-editor Settings Editor running at http://localhost:8090 Now open the URL in a web browser and you will see the Settings Editor's home-page. The first step is to select the HOP profiles. For this tutorial we will select the following: Core, Frontend, Deploy to Amazon Web Services and CI/CD. .. image:: img/settings-editor-profile-picker.png Now hit next to configure the rest of the project settings. .. image:: img/settings-editor-editor.png In order to make this tutorial as simple as possible edit the following configuration options: * ``project`` → ``name``: We will set the project name to ``hop-tutorial``. * ``project`` → ``profiles`` → ``aws`` → ``aws-vault`` → ``profile-prefix``: We will set the aws-vault profile to ``hop``. In the general case, you may want to use a prefix that identifies the project customer, or the AWS account where you will deploy the project, etc. * ``deployment-target`` → ``aws`` → ``account`` → ``region``: The AWS region where you want to create the project resources. Change to your desired region. So far the HOP CLI has been mainly tested on the ``eu-west-1`` region. So we recommend you to use that region in order to ensure that all the services required by HOP application will be available [#UsingOtherAWSRegion]_. Once you are done export the settings by clicking the "Export settings" button. The browser will download a ``settings.edn`` file that you will use in the next steps. .. note:: Make sure that the AWS region you configure is enabled in your AWS account. Not all the regions are enabled by default. Also, make sure that the AWS region you configure has the AWS Elastic Beanstalk service available. At the time of this writing, some of them (e.g., ``eu-south-2``) do not have it available. You can check the list of available regions at `AWS Elastic Beanstalk endpoints and quotas`_. .. _`AWS Elastic Beanstalk endpoints and quotas`: https://docs.aws.amazon.com/general/latest/gr/elasticbeanstalk.html .. warning:: If you already have an AWS account with existing resources, please refer to :doc:`/get-started/run-hop-application-on-aws/appendix/existing-aws-account-settings` document for further considerations. .. note:: If this is the second time you are following this tutorial, some of the AWS resources created the first time you run the tutorial will still exist. The HOP CLI does not delete any AWS resources, to avoid deleting resources that may be in use. The HOP CLI does not overwrite any existing resource either, for the same reason. This means you will need to delete those AWS resources manually yourself. Refer to :doc:`/how-to/delete-aws-resources/main` for additional details. .. _run-hop-application-on-aws_run-bootstrap-command: Run the bootstrap command ------------------------- .. note:: Make sure that the clock of the machine where you run the following commands is correctly synchronized. The AWS services APIs used by the HOP CLI perform signature validation. And those signatures include the local time of the machine. If that local time is off by more than a certain threshold, the requests are denied. Once you are happy with the selected configuration, you can bootstrap the project by running the following command, where you need to substitute ``your-aws-region`` by the AWS region you configured in the previous step. .. code-block:: console $ aws-vault exec --no-session --region your-aws-region hop/hop-tutorial-admin -- hop bootstrap new-project --settings-file-path hop-tutorial-settings.edn --target-project-dir hop-tutorial-project You are telling ``aws-vault`` to run the ``hop bootstrap`` command, with the ``hop/hop-tutorial-admin`` credentials that you stored in the vault in a previous step. Bootstrapping the project will take several minutes (mostly because of the AWS infrastructure provisioning). The tool will keep you informed about each step that it performs. Those steps are: * AWS infrastructure provisioning: * Account resources: Creates AWS resources that will be shared between multiple HOP projects. * Project resources: Creates AWS resources that will be shared between the different environments (test, production, etc.) inside the ``hop-tutorial`` project. * Dev environment resources: Creates the AWS resources needed for local development on your machine. * Test environment resources: Creates the AWS resources for deploying and running the test environment. * Project files creation: Creates the local project files in the ``hop-tutorial-project`` folder. * Post-installation steps: If required, the tool will print additional steps that have to be performed manually after the bootstrap process has been completed. Certain operations of the bootstrap process cannot be easily and securely automated. So the tool will print the steps that have to be performed manually. It is important not to lose the output of these post-installation steps until you have performed them all. Or to save that output in a secure place, because the output includes security credentials. The post-installation steps output includes all the details about what to do, but we will describe them here too. Nevertheless, you will still need to pay attention to `your` post-installation steps output, to get the actual credentials generated by the bootstrap process. Configure the local project credentials --------------------------------------- First you will have to add the credentials for the AWS user used for local development. This user will be shared among all the HOP projects you run on your system. So this step will only have to be performed the first time you bootstrap a HOP project. .. code-block:: console $ aws-vault add --add-config hop/hop-local-dev and provide the Access Key ID and Secret Access Key values provided in the post-installation steps output. Then you will have to configure the AWS IAM role used for running the ``hop-tutorial`` project. That role contains the specific permissions for interacting with the resources in the dev environment for the ``hop-tutorial`` project. You will have to edit the AWS client configuration file (usually in ``~/.aws/config``, but see `AWS CLI Configuration and credential file settings`_), and add the lines provided in the post-installation steps output. It should look like the following: .. _`AWS CLI Configuration and credential file settings`: https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html .. code-block:: ini # Example configuration [profile hop/hop-tutorial-dev-env] source_profile = hop/hop-local-dev role_arn = arn:aws:iam::your-aws-account-id:role/hop-tutorial-dev-role region = your-aws-region The tool will also print the Access Key ID and Secret Access Key for the CI/CD user. Take note of them, as you will need them in a later step to configure GitHub Actions. Initialize the Git repository ----------------------------- Before you make any changes to the code, we suggest to initialize the git repository with the project files. That way you will easily track any changes that you make to the files generated by the HOP CLI. .. code-block:: console $ cd hop-tutorial-project $ git init --initial-branch=main .. note:: If you have an older version of git that does not support the ``--initial-branch`` option, you can run the following commands instead: .. code-block:: console $ git init $ echo 'ref: refs/heads/main' > .git/HEAD As you can see, we are setting the git branch to ``main``, which is the default value in the settings file. This setting is configurable (under ``project`` → ``profiles`` → ``ci`` → ``continuous-deployment`` → ``git`` → ``deployment-branch.name``) and you can set it to any other desired value in the settings file. But it is important that the branch used in the above command aligns with the one configured in the settings file. Because the branch name configured in the settings file will be the only one used as the deployment branch by the CI/CD pipeline. Now you can make the initial commit: .. code-block:: console $ git add . $ git commit -m "Initial commit" Run the project in the local development environment ---------------------------------------------------- At this point you are ready to run the project in the local development environment. For that, simply run the ``start-dev.sh`` script. In short, the script will take care of three things: * Selecting the relevant `docker-compose` files used in the local development environment, and starting the Docker containers. * Making sure that the environment is started in a clean state. * Running the project with the ``hop/hop-tutorial-dev-env`` credentials, as they are needed to access the development environment AWS resources. When you run the ``start-dev.sh`` script, you will get all the log entries from the Docker containers: .. code-block:: console $ ./start-dev.sh ... ... lots of additional output ... ... app_1 | 2022-12-29T17:48:33.808145053Z nREPL server started on port 4001 on host 0.0.0.0 - nrepl://0.0.0.0:4001 You can see that the script started two Docker containers: the HOP web application and a `Nginx`_ reverse proxy. The final log line, when both containers have started and the application container is up and running, should say something like ``nREPL server started on port 4001 on host 0.0.0.0``. You can keep the script running and displaying new container log entries as they are produced, or you can stop the script execution. Stopping the script at this point does not have any unintended side effects like stopping the Docker containers, as they are started in the background. It simply stops displaying new container log entries. .. _Nginx: https://nginx.org The application container is up and running, but that does not mean that the application web server is up and running too. You need to start it manually from the REPL. In the application logs you should see that the REPL is running on the port ``4001``. Using your favorite Clojure(Script) IDE connect to the REPL. Some IDE's call this type of REPL external or remote. Once connected, you can load up the Duct development environment: .. code-block:: clojure user=> (dev) "WARNING: abs already refers to: #'clojure.core/abs in namespace: medley.core, being replaced by: #'medley.core/abs" "WARNING: update-keys already refers to: #'clojure.core/update-keys in namespace: io.aviso.exception, being replaced by: #'io.aviso.exception/update-keys" :loaded dev=> And then start the application itself: .. code-block:: clojure dev=> (go) :duct.server.http.jetty/starting-server {:port 3000} "WARNING: abs already refers to: #'clojure.core/abs in namespace: day8.re-frame-10x.inlined-deps.garden.v1v3v10.garden.color, being replaced by: #'day8.re-frame-10x.inlined-deps.garden.v1v3v10.garden.color/abs" "JavaScript environment will not launch automatically when :open-url is false" :initiated dev=> At this point the application's web server will be up and running, and you should be able to connect to it from a web browser. The ``(go)`` command outputted that the web server is listening on port ``3000``. But you should connect through the Nginx reverse proxy that is listening on port ``80`` instead. Open a web browser and go to ``http://localhost``. You should see HOP's welcome page there. .. image:: img/local-running-hop-app.png We will not make any change to the code now, so once you check that the application is running, you can stop the whole development environment by executing the following script: .. code-block:: console $ ./stop-dev.sh Create and configure the external GitHub repository --------------------------------------------------- In this tutorial we will use GitHub for hosting the code, and GitHub Actions for the CI/CD pipeline. Both services are free of charge for public repositories. You can follow GitHub's `official documentation`_ to create the repository. Once you create the repository, GitHub will display a set of instructions to do your first commit and push. You need to ignore them however. You just need to configure the secrets used to deploy the HOP application to AWS, by following these steps: 1. Open the repository settings. 2. In the sidebar, go to Security → Secrets → Actions. 3. You will need to configure three secrets here, with the values of the CI user provided in the post installation steps output. * ``AWS_ACCESS_KEY_ID`` * ``AWS_SECRET_ACCESS_KEY`` * ``AWS_DEFAULT_REGION`` .. image:: img/github-secrets.png Then configure the local git repository to point to GitHub's remote repository by execute the following command: .. code-block:: console $ git remote add origin And you can finally push your initial commit: .. code-block:: console $ git push -u origin main .. _official documentation: https://docs.github.com/en/repositories/creating-and-managing-repositories/creating-a-new-repository Deploy application to test environment -------------------------------------- The first push to the GitHub repository will not trigger the CI/CD pipeline. So you will have to make a second commit to proceed. Open the ``app/src/hop_tutorial/client/landing.cljs`` file using your favorite IDE and change the ``"Your application is up and running!"`` message to ``"Your application is up and running on AWS!!!!!!"``. Now commit and push the change to GitHub: .. code-block:: console $ git add app/src/hop_tutorial/client/landing.cljs $ git commit -m "Change landing message" $ git push This second commit will start the CI/CD pipeline, which will perform the following steps: 1. Run linting and formatting checks against the ``sh``, ``yaml``, ``json`` and ``Dockerfile`` files. 2. Run linting and formatting checks against the Clojure(Script) project files, using ``clj-kondo``, ``eastwood`` and ``cljfmt``. 3. Execute the project unit and integration tests. 4. Deploy the application to AWS Elastic Beanstalk test environment. In order to do that, the Docker production image is built and uploaded to AWS ECR first. .. image:: img/github-actions-pipeline-run.png Once the pipeline has run successfully, the application should be already deployed to AWS. You can check that the deployment was successfully done from the the AWS Console. Log in into your AWS Account and go to the AWS Elastic Beanstalk service. .. image:: img/elasticbeanstalk-environment-list.png Now choose the ``hop-tutorial-test`` environment under the ``hop-tutorial`` application. .. image:: img/elasticbeanstalk-environment.png If the deployment was success, you will see a green check in the Health section. It might happen that when you open the page the application is still being deployed to the actual EC2 instance where it will run. So do not worry if you see a red or yellow health check for some time. It takes from one to three minutes for the whole deployment process to be completed, and the application being up and running and ready to serve requests. If something goes wrong the errors will be displayed in the "Recent events" section, just below the health check section. Accessing the web application in the test environment ----------------------------------------------------- Under the AWS Elastic Beanstalk environment name you will see the application's publicly available URL. .. image:: img/elasticbeanstalk-environment-url.png When accessing it for the first time you will get a warning about the website SSL certificate being invalid. That is completely expected, as the web application is using a self-signed SSL certificate created by the HOP CLI. .. image:: img/self-signed-certificate-warning.png You can tell the browser to ignore the warning until you setup a real SSL certificate [#SetupSSLCertificate]_. .. image:: img/aws-running-hop-app.png There you have your new HOP-based application! .. rubric:: Footnotes .. [#AdminAwsVaultCreds] The tool provides secure storage for AWS credentials for the local development environment. The tool is also used in later steps for running the project locally, .. [#SettingsFileStructure] The file uses the `EDN`_ format, and it needs to conform to a HOP-specific `Malli Schema`_. Its structure is loosely based on GNU Emacs customization settings. .. [#UsingOtherAWSRegion] If you use any other AWS region and find any problem, please open an issue in the `HOP CLI issue tracker`_. .. [#SetupSSLCertificate] You will need to provision the SSL certificate in AWS Certificate Manager. And then configure the AWS Load Balancer used by the test environment, to use that new certificate. .. _`EDN`: https://github.com/edn-format/edn .. _`Malli Schema`: https://github.com/metosin/malli/ .. _`HOP CLI issue tracker`: https://github.com/gethop-dev/hop-cli/issues/