How To Configure SSL/TLS for MySQL on Ubuntu 18.04

Introduction

MySQL is the most popular open-source relational database management system in the world. While modern package managers have reduced some of the friction to getting MySQL up and running, there is still some further configuration that should be performed after you install it. One of the most important aspects to spend some extra time on is security.

By default, MySQL is configured to only accept local connections, or connections that originate from the same machine where MySQL is installed. If you need to access your MySQL database from a remote location, it’s important that you do so securely. In this guide, we will demonstrate how to configure MySQL on Ubuntu 18.04 to accept remote connections with SSL/TLS encryption.

Prerequisites

To complete this guide, you will need:

  • Two Ubuntu 18.04 servers. We will use one of these servers as the MySQL server while we’ll use the other as the client machine. Create a non-root user with sudo privileges and enable a firewall with ufw on each of these servers. Follow our Ubuntu 18.04 initial server setup guide to get both servers into the appropriate initial state.
  • On one of the machines, install and configure the MySQL server. Follow Steps 1 through 3 of our MySQL installation guide for Ubuntu 18.04 to do this. As you follow this guide, be sure to configure your root MySQL user to authenticate with a password, as described in Step 3 of the guide, as this is necessary to connect to MySQL using TCP rather than the local Unix socket.

Please note that throughout this guide, the server on which you installed MySQL will be referred to as the MySQL server and any commands that should be run on this machine will be shown with a blue background, like this:

Similarly, this guide will refer to the other server as the MySQL client and any commands that must be run on that machine will be shown with a red background:

Please keep these in mind as you follow along with this tutorial so as to avoid any confusion.

Step 1 — Checking MySQL’s Current SSL/TLS Status

Before you make any configuration changes, you can check the current SSL/TLS status on the MySQL server instance.

Use the following command to begin a MySQL session as the root MySQL user. This command includes the -p option, which instructs mysql to prompt you for a password in order to log in. It also includes the -h option which is used to specify the host to connect to. In this case it points it to 127.0.0.1, the IPv4 loopback interface also known as localhost. This will force the client to connect with TCP instead of using the local socket file. MySQL attempts to make connections through a Unix socket file by default. This is generally faster and more secure, since these connections can only be made locally and don’t have to go through all the checks and routing operations that TCP connections must perform. Connecting with TCP, however, allows us to check the SSL status of the connection:

  • mysql -u root -p -h 127.0.0.1

You will be prompted for the MySQL root password that you chose when you installed and configured MySQL. After entering it you’ll be dropped into an interactive MySQL session.

Show the state of the SSL/TLS variables issuing the following command:

  • SHOW VARIABLES LIKE '%ssl%';
Output
+---------------+----------+ | Variable_name | Value | +---------------+----------+ | have_openssl | DISABLED | | have_ssl | DISABLED | | ssl_ca | | | ssl_capath | | | ssl_cert | | | ssl_cipher | | | ssl_crl | | | ssl_crlpath | | | ssl_key | | +---------------+----------+ 9 rows in set (0.01 sec)

The have_openssl and have_ssl variables are both marked as DISABLED. This means that SSL functionality has been compiled into the server, but that it is not yet enabled.

Check the status of your current connection to confirm this:

  • \s
Output
-------------- mysql Ver 14.14 Distrib 5.7.26, for Linux (x86_64) using EditLine wrapper Connection id: 9 Current database: Current user: root@localhost SSL: Not in use Current pager: stdout Using outfile: '' Using delimiter: ; Server version: 5.7.26-0ubuntu0.18.04.1 (Ubuntu) Protocol version: 10 Connection: 127.0.0.1 via TCP/IP Server characterset: latin1 Db characterset: latin1 Client characterset: utf8 Conn. characterset: utf8 TCP port: 3306 Uptime: 40 min 11 sec Threads: 1 Questions: 33 Slow queries: 0 Opens: 113 Flush tables: 1 Open tables: 106 Queries per second avg: 0.013 --------------

As the above output indicates, SSL is not currently in use for this connection, even though you’re connected over TCP.

Close the current MySQL session when you are finished:

  • exit

Now that you’ve confirmed your MySQL server isn’t using SSL, you can move on to the next step where you will begin the process of enabling SSL by generating some certificates and keys. These will allow your server and client to communicate with one another securely.

Step 2 — Generating SSL/TLS Certificates and Keys

To enable SSL connections to MySQL, you first need to generate the appropriate certificate and key files. MySQL versions 5.7 and above provide a utility called mysql_ssl_rsa_setup that helps simplify this process. The version of MySQL you installed by following the prerequisite MySQL tutorial includes this utility, so we will use it here to generate the necessary files.

The MySQL process must be able to read the generated files, so use the --uid option to declare mysql as the system user that should own the generated files:

  • sudo mysql_ssl_rsa_setup --uid=mysql

This will produce output that looks similar to the following:

Output
Generating a 2048 bit RSA private key .+++ ..........+++ writing new private key to 'ca-key.pem' ----- Generating a 2048 bit RSA private key ........................................+++ ............+++ writing new private key to 'server-key.pem' ----- Generating a 2048 bit RSA private key .................................+++ ............................................................+++ writing new private key to 'client-key.pem' -----

These new files will be stored in MySQL’s data directory, located by default at /var/lib/mysql. Check the generated files by typing:

  • sudo find /var/lib/mysql -name '*.pem' -ls
Output
258930 4 -rw-r--r-- 1 mysql mysql 1107 May 3 16:43 /var/lib/mysql/client-cert.pem 258919 4 -rw-r--r-- 1 mysql mysql 451 May 3 16:43 /var/lib/mysql/public_key.pem 258925 4 -rw------- 1 mysql mysql 1675 May 3 16:43 /var/lib/mysql/server-key.pem 258927 4 -rw-r--r-- 1 mysql mysql 1107 May 3 16:43 /var/lib/mysql/server-cert.pem 258922 4 -rw------- 1 mysql mysql 1675 May 3 16:43 /var/lib/mysql/ca-key.pem 258928 4 -rw------- 1 mysql mysql 1675 May 3 16:43 /var/lib/mysql/client-key.pem 258924 4 -rw-r--r-- 1 mysql mysql 1107 May 3 16:43 /var/lib/mysql/ca.pem 258918 4 -rw------- 1 mysql mysql 1679 May 3 16:43 /var/lib/mysql/private_key.pem

These files are the key and certificate pairs for the certificate authority (starting with “ca”), the MySQL server process (starting with “server”), and for MySQL clients (starting with “client”). Additionally, the private_key.pem and public_key.pem files are used by MySQL to securely transfer passwords when not using SSL.

Now that you have the necessary certificate and key files, continue on to enable the use of SSL on your MySQL instance.

Step 3 — Enabling SSL Connections on the MySQL Server

Modern versions of MySQL look for the appropriate certificate files within the MySQL data directory whenever the server starts. Because of this, you won’t need to modify MySQL’s configuration to enable SSL.

Instead, enable SSL by restarting the MySQL service:

  • sudo systemctl restart mysql

After restarting, open up a new MySQL session using the same command as before. The MySQL client will automatically attempt to connect using SSL if it is supported by the server:

  • mysql -u root -p -h 127.0.0.1

Let’s take another look at the same information we requested last time. Check the values of the SSL-related variables:

  • SHOW VARIABLES LIKE '%ssl%';
Output
+---------------+-----------------+ | Variable_name | Value | +---------------+-----------------+ | have_openssl | YES | | have_ssl | YES | | ssl_ca | ca.pem | | ssl_capath | | | ssl_cert | server-cert.pem | | ssl_cipher | | | ssl_crl | | | ssl_crlpath | | | ssl_key | server-key.pem | +---------------+-----------------+ 9 rows in set (0.00 sec)

The have_openssl and have_ssl variables now read YES instead of DISABLED. Furthermore, the ssl_ca, ssl_cert, and ssl_key variables have been populated with the names of the respective files that we just generated.

Next, check the connection details again:

  • \s
Output
-------------- . . . SSL: Cipher in use is DHE-RSA-AES256-SHA . . . Connection: 127.0.0.1 via TCP/IP . . . --------------

This time, the specific SSL cipher is displayed, indicating that SSL is being used to secure the connection.

Exit back out to the shell:

  • exit

Your server is now capable of using encryption, but some additional configuration is required to allow remote access and mandate the use of secure connections.

Step 4 — Configuring Secure Connections for Remote Clients

Now that you’ve enabled SSL on the MySQL server, you can begin configuring secure remote access. To do this, you’ll configure your MySQL server to require that any remote connections be made over SSL, bind MySQL to listen on a public interface, and adjust your system’s firewall rules to allow external connections

Currently, the MySQL server is configured to accept SSL connections from clients. However, it will still allow unencrypted connections if requested by the client. We can change this by turning on the require_secure_transport option. This requires all connections to be made either with SSL or with a local Unix socket. Since Unix sockets are only accessible from within the server itself, the only connection option available to remote users will be with SSL.

To enable this setting, open the MySQL configuration file in your preferred text editor. Here, we’ll use nano:

  • sudo nano /etc/mysql/my.cnf

Inside there will be two !includedir directives which are used to source additional configuration files. You must add your own configuration beneath these lines so that it overrides any conflicting settings found in these additional configuration files.

Start by creating a [mysqld] section to target the MySQL server process. Under that section header, set require_secure_transport to ON, which will force MySQL to only allow secure connections:

/etc/mysql/my.cnf
. . .  !includedir /etc/mysql/conf.d/ !includedir /etc/mysql/mysql.conf.d/  [mysqld] # Require clients to connect either using SSL # or through a local socket file require_secure_transport = ON 

By default, MySQL is configured to only listen for connections that originate from 127.0.0.1, the loopback IP address that represents localhost. This means that MySQL is configured to only listen for connections that originate from the machine on which the MySQL server is installed.

In order to allow MySQL to listen for external connections, you must configure it to listen for connections on an external IP address. To do this, you can add the bind-address setting and point it to 0.0.0.0, a wildcard IP address that represents all IP addresses. Essentially, this will force MySQL to listen for connections on every interface:

/etc/mysql/my.cnf
. . .  !includedir /etc/mysql/conf.d/ !includedir /etc/mysql/mysql.conf.d/  [mysqld] # Require clients to connect either using SSL # or through a local socket file require_secure_transport = ON bind-address = 0.0.0.0 

Note: You could alternatively set bind-address to your MySQL server’s public IP address. However, you would need to remember to update your my.cnf file if you ever migrate your database to another machine.

After adding these lines, save and close the file. If you used nano to edit the file, you can do so by pressing CTRL+X, Y, then ENTER.

Next, restart MySQL to apply the new settings:

  • sudo systemctl restart mysql

Verify that MySQL is listening on 0.0.0.0 instead of 127.0.0.1 by typing:

  • sudo netstat -plunt

The output of this command will look like this:

Output
Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 0.0.0.0:3306 0.0.0.0:* LISTEN 13317/mysqld tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 1293/sshd tcp6 0 0 :::22 :::* LISTEN 1293/sshd

The 0.0.0.0 highlighted in the above output indicates that MySQL is listening for connections on all available interfaces.

Next, allow MySQL connections through your server’s firewall. Add an exception to your ufw rules by typing:

  • sudo ufw allow mysql
Output
Rule added Rule added (v6)

With that, remote connection attempts are now able to reach your MySQL server. However, you don’t currently have any users configured that can connect from a remote machine. We’ll create and configure a MySQL user that can connect from your client machine in the next step.

Step 5 — Creating a Dedicated MySQL User

At this point, your MySQL server will reject any attempt to connect from a remote client machine. This is because the existing MySQL users are all only configured to connect locally from the MySQL server. To resolve this, you will create a dedicated user that will only be able to connect from your client machine.

To create such a user, log back into MySQL as the root user:

  • mysql -u root -p

From the prompt, create a new remote user with the CREATE USER command. You can name this user whatever you’d like, but in this guide we name it mysql_user. Be sure to specify your client machine’s IP address in the host portion of the user specification to restrict connections to that machine and to replace password with a secure password of your choosing. Also, for some redundancy in case the require_secure_transport option is turned off in the future, specify that this user requires SSL by including the REQUIRE SSL clause, as shown here:

  • CREATE USER 'mysql_user'@'your_mysql_client_IP' IDENTIFIED BY 'password' REQUIRE SSL;

Next, grant the new user permissions on whichever databases or tables that they should have access to. To demonstrate, create an example database:

  • CREATE DATABASE example;

Then give your new user access to this database and all of its tables:

  • GRANT ALL ON example.* TO 'mysql_user'@'your_mysql_client_IP';

Next, flush the privileges to apply those settings immediately:

  • FLUSH PRIVILEGES;

Then exit back out to the shell when you are done:

  • exit

Your MySQL server is now set up to allow connections from your remote user. To test that you can connect to MySQL successfully, you will need to install the mysql-client package on the MySQL client.

Log in to your client machine with ssh

  • ssh sammy@your_mysql_client_ip

Then update the client machine’s package index:

  • sudo apt update

And install mysql-client with the following command:

  • sudo apt install mysql-client

When prompted, confirm the installation by pressing ENTER.

Once APT finishes installing the package, run the following command to test whether you can connect to the server successfully. This command includes the -u user option to specify mysql_user and the -h option to specify the MySQL server’s IP address:

  • mysql -u mysql_user -p -h your_mysql_server_IP

After submitting the password, you will be logged in to the remote server. Use \s to check the server’s status and confirm that your connection is secure:

  • \s
Output
-------------- . . . SSL: Cipher in use is DHE-RSA-AES256-SHA . . . Connection: your_mysql_server_IP via TCP/IP . . . --------------

Exit back out to the shell:

  • exit

You’ve confirmed that you’re able to connect to MySQL over SSL. However, you’ve not yet confirmed that the MySQL server is rejecting insecure connections. To test this, try connecting once more, but this time append --ssl-mode=disabled to the login command. This will instruct mysql-client to attempt an unencrypted connection:

  • mysql -u mysql_user -p -h mysql_server_IP --ssl-mode=disabled

After entering your password when prompted, your connection will be refused:

Output
ERROR 1045 (28000): Access denied for user 'mysql_user'@'mysql_server_IP' (using password: YES)

This shows that SSL connections are permitted while unencrypted connections are refused.

At this point, your MySQL server has been configured to accept secure remote connections. You can stop here if this satisfies your security requirements, but there are some additional pieces that you can put into place to enhance security and trust between your two servers.

Step 6 — (Optional) Configuring Validation for MySQL Connections

Currently, your MySQL server is configured with an SSL certificate signed by a locally generated certificate authority (CA). The server’s certificate and key pair are enough to provide encryption for incoming connections.

However, you aren’t yet fully leveraging the trust relationship that a certificate authority can provide. By distributing the CA certificate to clients — as well as the client certificate and key — both parties can provide proof that their certificates were signed by a mutually trusted certificate authority. This can help prevent spoofed connections from malicious servers.

In order to implement this extra, optional safeguard, we will transfer the appropriate SSL files to the client machine, create a client configuration file, and alter the remote MySQL user to require a trusted certificate.

Note: The process for transferring the CA certificate, client certificate, and client key to the MySQL client outlined in the following paragraphs involves displaying each file’s contents with cat, copying those contents to your clipboard, and pasting them in to a new file on the client machine. While it is possible to copy these files directly with a program like scp or sftp, this also requires you to set up SSH keys for both servers so as to allow them to communicate over SSH.

Our goal here is to keep the number of potential avenues for connecting to your MySQL server down to a minimum. While this process is slightly more laborious than directly transferring the files, it is equally secure and doesn’t require you to open an SSH connection between the two machines.

Begin by making a directory on the MySQL client in the home directory of your non-root user. Call this directory client-ssl:

  • mkdir ~/client-ssl

Because the certificate key is sensitive, lock down access to this directory so that only the current user can access it:

  • chmod 700 ~/client-ssl

On the MySQL server, display the contents of the CA certificate by typing:

  • sudo cat /var/lib/mysql/ca.pem
Output
-----BEGIN CERTIFICATE----- . . . -----END CERTIFICATE-----

Copy the entire output, including the BEGIN CERTIFICATE and END CERTIFICATE lines, to your clipboard.

On the MySQL client, create a file with the same name inside the new directory:

  • nano ~/client-ssl/ca.pem

Inside, paste the copied certificate contents from your clipboard. Save and close the file when you are finished.

Next, display the client certificate on the MySQL server:

  • sudo cat /var/lib/mysql/client-cert.pem
Output
-----BEGIN CERTIFICATE----- . . . -----END CERTIFICATE-----

Copy the file contents to your clipboard. Again, remember to include the first and last line.

Open a file with the same name on the MySQL client within the client-ssl directory:

  • nano ~/client-ssl/client-cert.pem

Paste the contents from your clipboard. Save and close the file.

Finally, display the contents of the client key file on the MySQL server:

  • sudo cat /var/lib/mysql/client-key.pem
Output
-----BEGIN RSA PRIVATE KEY----- . . . -----END RSA PRIVATE KEY-----

Copy the displayed contents, including the first and last line, to your clipboard.

On the MySQL client, open a file with the same name in the client-ssl directory:

  • nano ~/client-ssl/client-key.pem

Paste the contents from your clipboard. Save and close the file.

The client machine now has all of the credentials required to access the MySQL server. However, the MySQL server is still not set up to require trusted certificates for client connections.

To change this, log in to the MySQL root account again on the MySQL server:

  • mysql -u root -p

From here, change the security requirements for your remote user. Instead of the REQUIRE SSL clause, apply the REQUIRE X509 clause. This implies all of the security provided by the REQUIRE SSL clause, but additionally requires the connecting client to present a certificate signed by a certificate authority that the MySQL server trusts.

To adjust the user requirements, use the ALTER USER command:

  • ALTER USER 'mysql_user'@'mysql_client_IP' REQUIRE X509;

Then flush the changes to ensure that they are applied immediately:

  • FLUSH PRIVILEGES;

Exit back out to the shell when you are finished:

  • exit

Following that, check whether you can validate both parties when you connect.

On the MySQL client, first try to connect without providing the client certificates:

  • mysql -u mysql_user -p -h mysql_server_IP
Output
ERROR 1045 (28000): Access denied for user 'mysql_user'@'mysql_client_IP' (using password: YES)

As expected, the server rejects the connection when no client certificate is presented.

Now, connect while using the --ssl-ca, --ssl-cert, and --ssl-key options to point to the relevant files within the ~/client-ssl directory:

  • mysql -u mysql_user -p -h mysql_server_IP --ssl-ca=~/client-ssl/ca.pem --ssl-cert=~/client-ssl/client-cert.pem --ssl-key=~/client-ssl/client-key.pem

You’ve provided the client with the appropriate certificates and keys, so this attempt will be successful:

Log back out to regain access to your shell session:

  • exit

Now that you’ve confirmed access to the server, let’s implement a small usability improvement in order to avoid having to specify the certificate files each time you connect.

Inside your home directory on the MySQL client machine, create a hidden configuration file called ~/.my.cnf:

  • nano ~/.my.cnf

At the top of the file, create a section called [client]. Underneath, add the ssl-ca, ssl-cert, and ssl-key options and point them to the respective files you copied over from the server. It will look like this:

~/.my.cnf
[client] ssl-ca = ~/client-ssl/ca.pem ssl-cert = ~/client-ssl/client-cert.pem ssl-key = ~/client-ssl/client-key.pem 

The ssl-ca option tells the client to verify that the certificate presented by the MySQL server is signed by the certificate authority you pointed to. This allows the client to trust that it is connecting to a trusted MySQL server. Likewise, the ssl-cert and ssl-key options point to the files needed to prove to the MySQL server that it too has a certificate that has been signed by the same certificate authority. You’ll need this if you want the MySQL server to verify that the client was trusted by the CA as well.

Save and close the file when you are finished.

Now, you can connect to the MySQL server without adding the --ssl-ca, --ssl-cert, and --ssl-key options on the command line:

  • mysql -u remote_user -p -h mysql_server_ip

Your client and server will now each be presenting certificates when negotiating the connection. Each party is configured to verify the remote certificate against the CA certificate it has locally.

Conclusion

Your MySQL server is now configured to require secure connections from remote clients. Additionally, if you followed the steps to validate connections using the certificate authority, some level of trust is established by both sides that the remote party is legitimate.

DigitalOcean Community Tutorials

How To Build a Customer List Management App with React and TypeScript

The author selected the Tech Education Fund to receive a donation as part of the Write for DOnations program.

Introduction

TypeScript has brought a lot of improvement into how JavaScript developers structure and write code for apps, especially web applications. Defined as a superset of JavaScript, TypeScript behaves identically to JavaScript but with extra features designed to help developers build larger and more complex programs with fewer or no bugs. TypeScript is increasingly gaining popularity; adopted by major companies like Google for the Angular web framework. The Nest.js back-end framework was also built with TypeScript.

One of the ways to improve productivity as a developer is the ability to implement new features as quickly as possible without any concern over breaking the existing app in production. To achieve this, writing statically typed code is a style adopted by many seasoned developers. Statically typed programming languages like TypeScript enforce an association for every variable with a data type; such as a string, integer, boolean, and so on. One of the major benefits of using a statically typed programming language is that type checking is completed at compile time, therefore developers can see errors in their code at a very early stage.

React is an open-source JavaScript library, which developers use to create high-end user interfaces for scalable web applications. The great performance and dynamic user interfaces built with React for single-page applications make it a popular choice among developers.

In this tutorial, you will create a customer list management application with a separate REST API backend and a frontend built with React and TypeScript. You will build the backend using a fake REST API named json-server. You’ll use it to quickly set up a CRUD (Create, Read, Update, and Delete) backend. Consequently you can focus on handling the front-end logic of an application using React and TypeScript.

Prerequisites

To complete this tutorial, you will need:

Step 1 — Installing TypeScript and Creating the React Application

In this step, you will install the TypeScript package globally on your machine by using the Node Package Manager (npm). After that, you will also install React and its dependencies, and check that your React app is working by running the development server.

To begin, open a terminal and run the following command to install TypeScript:

  • npm install -g typescript

Once the installation process is complete, execute the following command to check your installation of TypeScript:

  • tsc -v

You will see the current version installed on your machine:

Output
Version 3.4.5

Next, you will install the React application by using the create-react-app tool to set up the application with a single command. You’ll use the npx command, which is a package runner tool that comes with npm 5.2+. The create-react-app tool has built-in support for working with TypeScript without any extra configuration required. Run the following command to create and install a new React application named typescript-react-app:

  • npx create-react-app typescript-react-app --typescript

The preceding command will create a new React application with the name typescript-react-app. The --typescript flag will set the default filetype for React components to .tsx.

Before you complete this section, the application will require moving from one port to another. To do that, you will need to install a routing library for your React application named React Router and its corresponding TypeScript definitions. You will use yarn to install the library and other packages for this project. This is because yarn is faster, especially for installing dependencies for a React application. Move into the newly created project folder and then install React Router with the following command:

  • cd typescript-react-app
  • yarn add react-router-dom

You now have the React Router package, which will provide the routing functionality within your project. Next, run the following command to install the TypeScript definitions for React Router:

  • yarn add @types/react-router-dom

Now you’ll install axios, which is a promised-based HTTP client for browsers, to help with the process of performing HTTP requests from the different components that you will create within the application:

  • yarn add axios

Once the installation process is complete, start the development server with:

  • yarn start

Your application will be running on http://localhost:3000.

React application homepage

You have successfully installed TypeScript, created a new React application, and installed React Router in order to help with navigating from one page of the application to another. In the next section, you will set up the back-end server for the application.

Step 2 — Creating a JSON Server

In this step, you’ll create a mock server that your React application can quickly connect with, as well as use its resources. It is important to note that this back-end service is not suitable for an application in production. You can use Nest.js, Express, or any other back-end technology to build a RESTful API in production. json-server is a useful tool whenever you need to create a prototype and mock a back-end server.

You can use either npm or yarn to install json-server on your machine. This will make it available from any directory of your project whenever you might need to make use of it. Open a new terminal window and run this command to install json-server while you are still within the project directory:

  • yarn global add json-server

Next, you will create a JSON file that will contain the data that will be exposed by the REST API. For the objects specified in this file (which you’ll create), a CRUD endpoint will be generated automatically. To begin, create a new folder named server and then move into it:

  • mkdir server
  • cd server

Now, use nano to create and open a new file named db.json:

  • nano db.json

Add the following content to the file:

/server/db.json
{     "customers": [         {             "id": 1,             "first_name": "Customer_1",             "last_name": "Customer_11",             "email": "customer1@mail.com",             "phone": "00000000000",             "address": "Customer_1 Address",             "description": "Customer_1 description"         },         {             "id": 2,             "first_name": "Customer_2",             "last_name": "Customer_2",             "email": "customer2@mail.com",             "phone": "00000000000",             "address": "Customer_2 Adress",             "description": "Customer_2 Description"         }     ] } 

The JSON structure consists of a customer object, which has two datasets assigned. Each customer consists of seven properties: id, description, first_name, last_name, email, phone, and address.

Save and exit the file.

By default, the json-server runs on port 3000—this is the same port on which your React application runs. To avoid conflict, you can change the default port for the json-server. To do that, move to the root directory of the application:

  • cd ~/typescript-react-app

Open the application with your preferred text editor and create a new file named json-server.json:

  • nano json-server.json

Now insert the following to update the port number:

/json-server.json
{     "port": 5000 } 

This will act as the configuration file for the json-server and it will ensure that the server runs on the port specified in it at all times.

Save and exit the file.

To run the server, use the following command:

  • json-server --watch server/db.json

This will start the json-server on port 5000. If you navigate to http://localhost:5000/customers in your browser, you will see the server showing your customer list.

Customer list shown by json-server

To streamline the process of running the json-server, you can update package.json with a new property named server to the scripts object as shown here:

/package.json
{ ...   "scripts": {     "start": "react-scripts start",     "build": "react-scripts build",     "test": "react-scripts test",     "eject": "react-scripts eject",     "server": "json-server --watch server/db.json"   }, ... } 

Save and exit the file.

Now anytime you wish to start the json-server, all you have to do is run yarn server from the terminal.

You’ve created a simple REST API that you will use as the back-end server for this application. You also created a customer JSON object that will be used as the default data for the REST API. Lastly, you configured an alternative port for the back-end server powered by json-server. Next, you will build reusable components for your application.

Step 3 — Creating Reusable Components

In this section, you will create the required React components for the application. This will include components to create, display, and edit the details of a particular customer in the database respectively. You’ll also build some of the TypeScript interfaces for your application.

To begin, move back to the terminal where you have the React application running and stop the development server with CTRL + C. Next, navigate to the ./src/ folder:

  • cd ./src/

Then, create a new folder named components inside of it and move into the new folder:

  • mkdir components
  • cd components

Within the newly created folder, create a customer folder and then move into it:

  • mkdir customer
  • cd customer

Now create two new files named Create.tsx and Edit.tsx:

  • touch Create.tsx Edit.tsx

These files are React reusable components that will render the forms and hold all the business logic for creating and editing the details of a customer respectively.

Open the Create.tsx file in your text editor and add the following code:

/src/components/customer/Create.tsx
import * as React from 'react'; import axios from 'axios'; import { RouteComponentProps, withRouter } from 'react-router-dom';  export interface IValues {     first_name: string,     last_name: string,     email: string,     phone: string,     address: string,     description: string, } export interface IFormState {     [key: string]: any;     values: IValues[];     submitSuccess: boolean;     loading: boolean; }  

Here you’ve imported React, axios, and other required components necessary for routing from the React Router package. After that you created two new interfaces named IValues and IFormState. TypeScript interfaces help to define the specific type of values that should be passed to an object and enforce consistency throughout an application. This ensures that bugs are less likely to appear in your program.

Next, you will build a Create component that extends React.Component. Add the following code to the Create.tsx file immediately after the IFormState interface:

/src/components/customer/Create.tsx
... class Create extends React.Component<RouteComponentProps, IFormState> {     constructor(props: RouteComponentProps) {         super(props);         this.state = {             first_name: '',             last_name: '',             email: '',             phone: '',             address: '',             description: '',             values: [],             loading: false,             submitSuccess: false,         }     } } export default withRouter(Create) 

Here you’ve defined a React component in Typescript. In this case, the Create class component accepts props (short for “properties”) of type RouteComponentProps and uses a state of type IFormState. Then, inside the constructor, you initialized the state object and defined all the variables that will represent the rendered values for a customer.

Next, add these methods within the Create class component, just after the constructor. You’ll use these methods to process customer forms and handle all changes in the input fields:

/src/components/customer/Create.tsx
...           values: [],           loading: false,           submitSuccess: false,       }   }    private processFormSubmission = (e: React.FormEvent<HTMLFormElement>): void => {           e.preventDefault();           this.setState({ loading: true });           const formData = {               first_name: this.state.first_name,               last_name: this.state.last_name,               email: this.state.email,               phone: this.state.phone,               address: this.state.address,               description: this.state.description,           }           this.setState({ submitSuccess: true, values: [...this.state.values, formData], loading: false });           axios.post(`http://localhost:5000/customers`, formData).then(data => [               setTimeout(() => {                   this.props.history.push('/');               }, 1500)           ]);       }        private handleInputChanges = (e: React.FormEvent<HTMLInputElement>) => {           e.preventDefault();           this.setState({               [e.currentTarget.name]: e.currentTarget.value,       })   }  ... export default withRouter(Create) ... 

The processFormSubmission() method receives the details of the customer from the application state and posts it to the database using axios. The handleInputChanges() uses React.FormEvent to obtain the values of all input fields and calls this.setState() to update the state of the application.

Next, add the render() method within the Create class component immediately after the handleInputchanges() method. This render() method will display the form to create a new customer in the application:

/src/components/customer/Create.tsx
...   public render() {       const { submitSuccess, loading } = this.state;       return (           <div>               <div className={"col-md-12 form-wrapper"}>                   <h2> Create Post </h2>                   {!submitSuccess && (                       <div className="alert alert-info" role="alert">                           Fill the form below to create a new post                   </div>                   )}                   {submitSuccess && (                       <div className="alert alert-info" role="alert">                           The form was successfully submitted!                           </div>                   )}                   <form id={"create-post-form"} onSubmit={this.processFormSubmission} noValidate={true}>                       <div className="form-group col-md-12">                           <label htmlFor="first_name"> First Name </label>                           <input type="text" id="first_name" onChange={(e) => this.handleInputChanges(e)} name="first_name" className="form-control" placeholder="Enter customer's first name" />                       </div>                       <div className="form-group col-md-12">                           <label htmlFor="last_name"> Last Name </label>                           <input type="text" id="last_name" onChange={(e) => this.handleInputChanges(e)} name="last_name" className="form-control" placeholder="Enter customer's last name" />                       </div>                       <div className="form-group col-md-12">                           <label htmlFor="email"> Email </label>                           <input type="email" id="email" onChange={(e) => this.handleInputChanges(e)} name="email" className="form-control" placeholder="Enter customer's email address" />                       </div>                       <div className="form-group col-md-12">                           <label htmlFor="phone"> Phone </label>                           <input type="text" id="phone" onChange={(e) => this.handleInputChanges(e)} name="phone" className="form-control" placeholder="Enter customer's phone number" />                       </div>                       <div className="form-group col-md-12">                           <label htmlFor="address"> Address </label>                           <input type="text" id="address" onChange={(e) => this.handleInputChanges(e)} name="address" className="form-control" placeholder="Enter customer's address" />                       </div>                       <div className="form-group col-md-12">                           <label htmlFor="description"> Description </label>                           <input type="text" id="description" onChange={(e) => this.handleInputChanges(e)} name="description" className="form-control" placeholder="Enter Description" />                       </div>                       <div className="form-group col-md-4 pull-right">                           <button className="btn btn-success" type="submit">                               Create Customer                           </button>                           {loading &&                               <span className="fa fa-circle-o-notch fa-spin" />                           }                       </div>                   </form>               </div>           </div>       )   } ... 

Here, you created a form with the input fields to hold the values of the first_name, last_name, email, phone, address, and description of a customer. Each of the input fields have a method handleInputChanges() that runs on every keystroke, updating the React state with the value it obtains from the input field. Furthermore, depending on the state of the application, a boolean variable named submitSuccess will control the message that the application will display before and after creating a new customer.

You can see the complete code for this file in this GitHub repository.

Save and exit Create.tsx.

Now that you have added the appropriate logic to the Create component file for the application, you’ll proceed to add contents for the Edit component file.

Open your Edit.tsx file within the customer folder, and start by adding the following content to import React, axios, and also define TypeScript interfaces:

/src/components/customer/Edit.tsx
import * as React from 'react'; import { RouteComponentProps, withRouter } from 'react-router-dom'; import axios from 'axios';  export interface IValues {     [key: string]: any; } export interface IFormState {     id: number,     customer: any;     values: IValues[];     submitSuccess: boolean;     loading: boolean; } 

Similarly to the Create component, you import the required modules and create IValues and IFormState interfaces respectively. The IValues interface defines the data type for the input fields’ values, while you’ll use IFormState to declare the expected type for the state object of the application.

Next, create the EditCustomer class component directly after the IFormState interface block as shown here:

/src/components/customer/Edit.tsx
... class EditCustomer extends React.Component<RouteComponentProps<any>, IFormState> {     constructor(props: RouteComponentProps) {         super(props);         this.state = {             id: this.props.match.params.id,             customer: {},             values: [],             loading: false,             submitSuccess: false,         }     } } export default withRouter(EditCustomer) 

This component takes the RouteComponentProps<any> and an interface of IFormState as a parameter. You use the addition of <any> to the RouteComponentProps because whenever React Router parses path parameters, it doesn’t do any type conversion to ascertain whether the type of the data is number or string. Since you’re expecting a parameter for uniqueId of a customer, it is safer to use any.

Now add the following methods within the component:

/src/components/customer/Edit.tsx
...     public componentDidMount(): void {         axios.get(`http://localhost:5000/customers/$  {this.state.id}`).then(data => {             this.setState({ customer: data.data });         })     }      private processFormSubmission = async (e: React.FormEvent<HTMLFormElement>): Promise<void> => {         e.preventDefault();         this.setState({ loading: true });         axios.patch(`http://localhost:5000/customers/$  {this.state.id}`, this.state.values).then(data => {             this.setState({ submitSuccess: true, loading: false })             setTimeout(() => {                 this.props.history.push('/');             }, 1500)         })     }      private setValues = (values: IValues) => {         this.setState({ values: { ...this.state.values, ...values } });     }     private handleInputChanges = (e: React.FormEvent<HTMLInputElement>) => {         e.preventDefault();         this.setValues({ [e.currentTarget.id]: e.currentTarget.value })     } ... }  export default withRouter(EditCustomer) 

First, you add a componentDidMount() method, which is a lifecycle method that is being called when the component is created. The method takes the id obtained from the route parameter to identify a particular customer as a parameter, uses it to retrieve their details from the database and then populates the form with it. Furthermore, you add methods to process form submission and handle changes made to the values of the input fields.

Lastly, add the render() method for the Edit component:

/src/components/customer/Edit.tsx
...     public render() {         const { submitSuccess, loading } = this.state;         return (             <div className="App">                 {this.state.customer &&                     <div>                         < h1 > Customer List Management App</h1>                         <p> Built with React.js and TypeScript </p>                          <div>                             <div className={"col-md-12 form-wrapper"}>                                 <h2> Edit Customer </h2>                                 {submitSuccess && (                                     <div className="alert alert-info" role="alert">                                         Customer's details has been edited successfully </div>                                 )}                                 <form id={"create-post-form"} onSubmit={this.processFormSubmission} noValidate={true}>                                     <div className="form-group col-md-12">                                         <label htmlFor="first_name"> First Name </label>                                         <input type="text" id="first_name" defaultValue={this.state.customer.first_name} onChange={(e) => this.handleInputChanges(e)} name="first_name" className="form-control" placeholder="Enter customer's first name" />                                     </div>                                     <div className="form-group col-md-12">                                         <label htmlFor="last_name"> Last Name </label>                                         <input type="text" id="last_name" defaultValue={this.state.customer.last_name} onChange={(e) => this.handleInputChanges(e)} name="last_name" className="form-control" placeholder="Enter customer's last name" />                                     </div>                                     <div className="form-group col-md-12">                                         <label htmlFor="email"> Email </label>                                         <input type="email" id="email" defaultValue={this.state.customer.email} onChange={(e) => this.handleInputChanges(e)} name="email" className="form-control" placeholder="Enter customer's email address" />                                     </div>                                     <div className="form-group col-md-12">                                         <label htmlFor="phone"> Phone </label>                                         <input type="text" id="phone" defaultValue={this.state.customer.phone} onChange={(e) => this.handleInputChanges(e)} name="phone" className="form-control" placeholder="Enter customer's phone number" />                                     </div>                                     <div className="form-group col-md-12">                                         <label htmlFor="address"> Address </label>                                         <input type="text" id="address" defaultValue={this.state.customer.address} onChange={(e) => this.handleInputChanges(e)} name="address" className="form-control" placeholder="Enter customer's address" />                                     </div>                                     <div className="form-group col-md-12">                                         <label htmlFor="description"> Description </label>                                         <input type="text" id="description" defaultValue={this.state.customer.description} onChange={(e) => this.handleInputChanges(e)} name="description" className="form-control" placeholder="Enter Description" />                                     </div>                                     <div className="form-group col-md-4 pull-right">                                         <button className="btn btn-success" type="submit">                                             Edit Customer </button>                                         {loading &&                                             <span className="fa fa-circle-o-notch fa-spin" />                                         }                                     </div>                                 </form>                             </div>                         </div>                     </div>                 }             </div>         )     } ...     

Here, you created a form to edit the details of a particular customer, and then populated the input fields within that form with the customer’s details that your application’s state obtained. Similarly to the Create component, changes made to all the input fields will be handled by the handleInputChanges() method.

You can see the complete code for this file in this GitHub repository.

Save and exit Edit.tsx.

To view the complete list of customers created within the application, you’ll create a new component within the ./src/components folder and name it Home.tsx:

  • cd ./src/components
  • nano Home.tsx

Add the following content:

/src/components/Home.tsx
import * as React from 'react'; import { Link, RouteComponentProps } from 'react-router-dom'; import axios from 'axios';  interface IState {     customers: any[]; }  export default class Home extends React.Component<RouteComponentProps, IState> {     constructor(props: RouteComponentProps) {         super(props);         this.state = { customers: [] }     }     public componentDidMount(): void {         axios.get(`http://localhost:5000/customers`).then(data => {             this.setState({ customers: data.data })         })     }     public deleteCustomer(id: number) {         axios.delete(`http://localhost:5000/customers/$  {id}`).then(data => {             const index = this.state.customers.findIndex(customer => customer.id === id);             this.state.customers.splice(index, 1);             this.props.history.push('/');         })     } } 

Here, you’ve imported React, axios, and other required components from React Router. You created two new methods within the Home component:

  • componentDidMount(): The application invokes this method immediately after a component is mounted. Its responsibility here is to retrieve the list of customers and update the home page with it.
  • deleteCustomer(): This method will accept an id as a parameter and will delete the details of the customer identified with that id from the database.

Now add the render() method to display the table that holds the list of customers for the Home component:

/src/components/Home.tsx
... public render() {         const customers = this.state.customers;         return (             <div>                 {customers.length === 0 && (                     <div className="text-center">                         <h2>No customer found at the moment</h2>                     </div>                 )}                 <div className="container">                     <div className="row">                         <table className="table table-bordered">                             <thead className="thead-light">                                 <tr>                                     <th scope="col">Firstname</th>                                     <th scope="col">Lastname</th>                                     <th scope="col">Email</th>                                     <th scope="col">Phone</th>                                     <th scope="col">Address</th>                                     <th scope="col">Description</th>                                     <th scope="col">Actions</th>                                 </tr>                             </thead>                             <tbody>                                 {customers && customers.map(customer =>                                     <tr key={customer.id}>                                         <td>{customer.first_name}</td>                                         <td>{customer.last_name}</td>                                         <td>{customer.email}</td>                                         <td>{customer.phone}</td>                                         <td>{customer.address}</td>                                         <td>{customer.description}</td>                                         <td>                                             <div className="d-flex justify-content-between align-items-center">                                                 <div className="btn-group" style={{ marginBottom: "20px" }}>                                                     <Link to={`edit/$  {customer.id}`} className="btn btn-sm btn-outline-secondary">Edit Customer </Link>                                                     <button className="btn btn-sm btn-outline-secondary" onClick={() => this.deleteCustomer(customer.id)}>Delete Customer</button>                                                 </div>                                             </div>                                         </td>                                     </tr>                                 )}                             </tbody>                         </table>                     </div>                 </div>             </div>         )     } ... 

In this code block, you retrieve the lists of customers from the application’s state as an array, iterate over it, and display it within an HTML table. You also add the customer.id parameter, which the method uses to identify and delete the details of a particular customer from the list.

Save and exit Home.tsx.

You’ve adopted a statically typed principle for all the components created with this application by defining types for the components and props through the use of interfaces. This is one of the best approaches to using TypeScript for a React application.

With this, you’ve finished creating all the required reusable components for the application. You can now update the app component with links to all the components that you have created so far.

Step 4 — Setting Up Routing and Updating the Entry Point of the Application

In this step, you will import the necessary components from the React Router package and configure the App component to render different components depending on the route that is loaded. This will allow you to navigate through different pages of the application. Once a user visits a route, for example /create, React Router will use the path specified to render the contents and logic within the appropriate component defined to handle such route.

Navigate to ./src/App.tsx:

  • nano App.tsx

Then replace its content with the following:

/src/App.tsx
import * as React from 'react'; import './App.css'; import { Switch, Route, withRouter, RouteComponentProps, Link } from 'react-router-dom'; import Home from './components/Home'; import Create from './components/customer/Create'; import EditCustomer from './components/customer/Edit';  class App extends React.Component<RouteComponentProps<any>> {   public render() {     return (       <div>         <nav>           <ul>             <li>               <Link to={'/'}> Home </Link>             </li>             <li>               <Link to={'/create'}> Create Customer </Link>             </li>           </ul>         </nav>         <Switch>           <Route path={'/'} exact component={Home} />           <Route path={'/create'} exact component={Create} />           <Route path={'/edit/:id'} exact component={EditCustomer} />         </Switch>       </div>     );   } } export default withRouter(App); 

You imported all the necessary components from the React Router package and you also imported the reusable components for creating, editing, and viewing customers’ details.

Save and exit App.tsx.

The ./src/index.tsx file is the entry point for this application and renders the application. Open this file and import React Router into it, then wrap the App component inside a BrowserRouter:

/src/index.tsx
import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; import App from './App'; import { BrowserRouter } from 'react-router-dom';  import * as serviceWorker from './serviceWorker'; ReactDOM.render(     <BrowserRouter>         <App />     </BrowserRouter>     , document.getElementById('root') ); serviceWorker.unregister(); 

React Router uses the BrowserRouter component to make your application aware of the navigation, such as history and current path.

Once you’ve finished editing Index.tsx, save and exit.

Lastly, you will use Bootstrap to add some style to your application. Bootstrap is a popular HTML, CSS, and JavaScript framework for developing responsive, mobile-first projects on the web. It allows developers to build an appealing user interface without having to write too much CSS. It comes with a responsive grid system that gives a web page a finished look that works on all devices.

To include Bootstrap and styling for your application, replace the contents of ./src/App.css with the following:

/src/App.css
@import 'https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css';  .form-wrapper {   width: 500px;   margin: 0 auto; } .App {   text-align: center;   margin-top: 30px; } nav {   width: 300px;   margin: 0 auto;   background: #282c34;   height: 70px;   line-height: 70px; } nav ul li {   display: inline;   list-style-type: none;   text-align: center;   padding: 30px; } nav ul li a {   margin: 50px 0;   font-weight: bold;   color: white;   text-decoration: none; } nav ul li a:hover {   color: white;   text-decoration: none; } table {   margin-top: 50px; } .App-link {   color: #61dafb; } @keyframes App-logo-spin {   from {     transform: rotate(0deg);   }   to {     transform: rotate(360deg);   } } 

You have used Bootstrap here to enhance the look and feel of the application by giving it a default layout, styles, and color. You have also added some custom styles, particularly to the navigation bar.

Save and exit App.css.

In this section, you have configured React Router to render the appropriate component depending on the route visited by the user and also added some styling to make the application more attractive to users. Next, you will test all the functionality implemented for the application.

Step 5 — Running Your Application

Now that you have set up the frontend of this application with React and TypeScript by creating several reusable components, and also built a REST API with the json-server, you can run your app.

Navigate back to the project’s root folder:

  • cd ~/typescript-react-app

Next run the following command to start your app:

  • yarn start

Note: Make sure your server is still running in the other terminal window. Otherwise, start it with: yarn server.

Navigate to http://localhost:3000 to view the application from your browser. Then proceed to click on the Create button and fill in the details of a customer.

Create customer page

After entering the appropriate values in the input fields, click on the Create Customer button to submit the form. The application will redirect you back to your homepage once you’re done creating a new customer.

View customers page

Click the Edit Customer button for any of the rows and you will be directed to the page that hosts the editing functionality for the corresponding customer on that row.

Edit customer page

Edit the details of the customer and then click on Edit Customer to update the customer’s details.

You’ve run your application to ensure all the components are working. Using the different pages of your application, you’ve created and edited a customer entry.

Conclusion

In this tutorial you built a customer list management app with React and TypeScript. The process in this tutorial is a deviation from using JavaScript as the conventional way of structuring and building applications with React. You’ve leveraged the benefits of using TypeScript to complete this front-end focused tutorial.

To continue to develop this project, you can move your mock back-end server to a production-ready back-end technology like Express or Nest.js. Furthermore, you can extend what you have built in this tutorial by adding more features such as authentication and authorization with different tools like the Passport.js authentication library.

You can find the complete source code for the project on GitHub.

DigitalOcean Community Tutorials

Como Instalar e Utilizar o TensorFlow no Ubuntu 18.04

Introdução

O TensorFlow é um software open-source de aprendizado de máquina ou machine learning usado para treinar redes neurais. As redes neurais do TensorFlow são expressas sob a forma de gráficos de fluxo de dados stateful. Cada nó no gráfico representa as operações realizadas por redes neurais em matrizes multidimensionais. Essas matrizes multidimensionais são comumente conhecidas como “tensores”, daí o nome TensorFlow.

O TensorFlow é um sistema de software de deep learning. Ele funciona bem para a recuperação de informações, conforme demonstrado pelo Google na forma como eles pesquisam o ranking em seu sistema de inteligência artificial de machine learning, RankBrain. O TensorFlow pode realizar reconhecimento de imagem, conforme mostrado no Inception do Google, bem como reconhecimento de áudio em linguagem humana. Também é útil na resolução de outros problemas não específicos de machine learning, como equações diferenciais parciais.

A arquitetura do TensorFlow permite o deployment em várias CPUs ou GPUs em um desktop, servidor ou dispositivo móvel. Existem também extensões para integração com CUDA, uma plataforma de computação paralela da Nvidia. Isso fornece aos usuários que estão fazendo deploy em uma GPU acesso direto ao conjunto de instruções virtuais e outros elementos da GPU que são necessários para tarefas computacionais paralelas.

Neste tutorial, vamos instalar a versão “CPU support only” do TensorFlow. Essa instalação é ideal para pessoas que querem instalar e usar o TensorFlow, mas que não têm uma placa de vídeo Nvidia ou não precisam executar aplicações críticas em termos de desempenho.

Você pode instalar o TensorFlow de várias maneiras. Cada método tem um caso de uso e um ambiente de desenvolvimento diferentes:

  • Python e Virtualenv: Nesta abordagem, você instala o TensorFlow e todos os pacotes necessários para utilizá-lo em um ambiente virtual do Python. Isso isola seu ambiente do TensorFlow de outros programas do Python na mesma máquina.
  • Native pip: Neste método, você instala o TensorFlow em seu sistema de maneira global. Isso é recomendado para pessoas que querem disponibilizar o TensorFlow para todos em um sistema multiusuário. Esse método de instalação não isola o TensorFlow em um ambiente de contenção e pode interferir em outras instalações ou bibliotecas do Python.
  • Docker: O Docker é um ambiente runtime de container e isola completamente seu conteúdo dos pacotes preexistentes em seu sistema. Nesse método, você usa um container do Docker que contém o TensorFlow e todas as suas dependências. Esse método é ideal para incorporar o TensorFlow a uma arquitetura de aplicações maior que já usa o Docker. No entanto, o tamanho da imagem do Docker será bem grande.

Neste tutorial, você instalará o TensorFlow em um ambiente virtual do Python com virtualenv. Essa abordagem isola a instalação do TensorFlow e coloca tudo em funcionamento rapidamente. Depois de concluir a instalação, você fará a sua validação executando um pequeno programa do TensorFlow e, em seguida, utilizará o TensorFlow para realizar o reconhecimento de imagem.

Pré-requisitos

Antes de começar este tutorial, você precisará do seguinte:

Passo 1 — Instalando o TensorFlow

Nesta etapa, vamos criar um ambiente virtual e instalar o TensorFlow.

Primeiro, crie um diretório de projeto. Vamos chamá-lo de tf-demo para fins de demonstração, mas escolha um nome de diretório que seja significativo para você:

  • mkdir ~/tf-demo

Navegue até o seu diretório tf-demo recém-criado:

  • cd ~/tf-demo

Em seguida, crie um novo ambiente virtual chamado tensorflow-dev, por exemplo. Execute o seguinte comando para criar o ambiente:

  • python3 -m venv tensorflow-dev

Isto cria um novo diretório tensorflow-dev que conterá todos os pacotes que você instalar enquanto este ambiente estiver ativado. Também inclui o pip e uma versão independente do Python.

Agora ative seu ambiente virtual:

  • source tensorflow-dev/bin/activate

Uma vez ativado, você verá algo semelhante a isso no seu terminal:

(tensorflow-dev)nome_de_usuário@hostname:~/tf-demo $   

Agora você pode instalar o TensorFlow em seu ambiente virtual.

Execute o seguinte comando para instalar e atualizar para a versão mais recente do TensorFlow disponível em PyPi:

  • pip install --upgrade tensorflow

O TensorFlow será instalado, e você deverá obter uma saída que indique que a instalação, juntamente com quaisquer pacotes dependentes, foi bem-sucedida.

Output
... Successfully installed absl-py-0.7.1 astor-0.7.1 gast-0.2.2 grpcio-1.19.0 h5py-2.9.0 keras-applications-1.0.7 keras-preprocessing-1.0.9 markdown-3.0.1 mock-2.0.0 numpy-1.16.2 pbr-5.1.3 protobuf-3.7.0 setuptools-40.8.0 tensorboard-1.13.1 tensorflow-1.13.1 tensorflow-estimator-1.13.0 termcolor-1.1.0 werkzeug-0.15.0 wheel-0.33.1 ... Successfully installed bleach-1.5.0 enum34-1.1.6 html5lib-0.9999999 markdown-2.6.9 numpy-1.13.3 protobuf-3.5.0.post1 setuptools-38.2.3 six-1.11.0 tensorflow-1.4.0 tensorflow-tensorboard-0.4.0rc3 werkzeug-0.12.2 wheel-0.30.0

Você pode desativar seu ambiente virtual a qualquer momento usando o seguinte comando:

  • deactivate

Para reativar o ambiente posteriormente, navegue até o diretório do projeto e execute source tensorflow-dev/bin/activate.

Agora que você instalou o TensorFlow, vamos garantir que a instalação esteja funcionando.

Passo 2 — Validando a Instalação

Para validar a instalação do TensorFlow, vamos executar um programa simples como um usuário não-root. Usaremos o exemplo canônico de iniciante de “Hello, world!” como uma forma de validação. Em vez de criar um arquivo em Python, criaremos esse programa usando o console interativo do Python.

Para escrever o programa, inicie seu interpretador Python:

  • python

Você verá o seguinte prompt aparecer no seu terminal:

>>> 

Este é o prompt do interpretador Python e indica que ele está pronto para que você comece a inserir algumas instruções do Python.

Primeiro, digite esta linha para importar o pacote TensorFlow e disponibilizá-lo como a variável local tf. Pressione ENTER depois de digitar a linha de código:

  • import tensorflow as tf

Em seguida, adicione esta linha de código para definir a mensagem “Hello, world!”:

  • hello = tf.constant("Hello, world!")

Em seguida, crie uma nova sessão do TensorFlow e atribua-a à variável sess:

  • sess = tf.Session()

Nota: Dependendo do seu ambiente, você poderá ver esta saída:

Output
2019-03-20 16:22:45.956946: W tensorflow/core/platform/cpu_feature_guard.cc:45] The TensorFlow library wasn't compiled to use SSE4.1 instructions, but these are available on your machine and could speed up CPU computations. 2019-03-20 16:22:45.957158: W tensorflow/core/platform/cpu_feature_guard.cc:45] The TensorFlow library wasn't compiled to use SSE4.2 instructions, but these are available on your machine and could speed up CPU computations. 2019-03-20 16:22:45.957282: W tensorflow/core/platform/cpu_feature_guard.cc:45] The TensorFlow library wasn't compiled to use AVX instructions, but these are available on your machine and could speed up CPU computations. 2019-03-20 16:22:45.957404: W tensorflow/core/platform/cpu_feature_guard.cc:45] The TensorFlow library wasn't compiled to use AVX2 instructions, but these are available on your machine and could speed up CPU computations. 2019-03-20 16:22:45.957527: W tensorflow/core/platform/cpu_feature_guard.cc:45] The TensorFlow library wasn't compiled to use FMA instructions, but these are available on your machine and could speed up CPU computations.

Isso lhe diz que você tem um conjunto de instruções que tem o potencial para ser otimizado para um melhor desempenho com o TensorFlow. Se você vir isso, poderá ignorar com segurança e continuar.

Por fim, insira essa linha de código para imprimir o resultado da execução da sessão hello do TensorFlow que você construiu nas suas linhas de código anteriores:

No Python 3, sess.run() irá retornar uma byte string, que será renderizada como b'Hello, world!' se você executar print(sess.run(hello)) sozinho. Para retornar Hello, world! como uma string, vamos adicionar o método decode().

  • print(sess.run(hello).decode())

Você verá essa saída no seu console:

Output
Hello, world!

Isso indica que tudo está funcionando e que você pode começar a usar o TensorFlow.

Saia do console interativo do Python pressionando CTRL+D ou digitando quit().

A seguir, vamos usar a API de reconhecimento de imagem do TensorFlow para nos familiarizarmos com o TensorFlow.

Passo 3 — Usando o TensorFlow para Reconhecimento de Imagem

Agora que o TensorFlow está instalado e você o validou executando um programa simples, podemos dar uma olhada em seus recursos de reconhecimento de imagem.

Para classificar uma imagem, você precisa treinar um modelo. Então você precisa escrever algum código para usar o modelo. Para aprender mais sobre conceitos de machine learning, considere a leitura de “An Introduction to Machine Learning.”

O TensorFlow fornece um repositório de modelos e exemplos, incluindo código e um modelo treinado para classificar imagens.

Use o Git para clonar o repositório de modelos do TensorFlow do GitHub no diretório do seu projeto:

  • git clone https://github.com/tensorflow/models.git

Você receberá a seguinte saída enquanto o Git clona o repositório em uma nova pasta chamada models:

Output
Cloning into 'models'... remote: Enumerating objects: 32, done. remote: Counting objects: 100% (32/32), done. remote: Compressing objects: 100% (26/26), done. remote: Total 24851 (delta 17), reused 12 (delta 6), pack-reused 24819 Receiving objects: 100% (24851/24851), 507.78 MiB | 32.73 MiB/s, done. Resolving deltas: 100% (14629/14629), done. Checking out files: 100% (2858/2858), done.

Mude para o diretório models/tutorials/image/imagenet:

  • cd models/tutorials/image/imagenet

Este diretório contém o arquivo classify_image.py que usa o TensorFlow para reconhecer imagens. Este programa faz o download de um modelo treinado a partir de tensorflow.org em sua primeira execução. O download desse modelo exige que você tenha 200 MB de espaço livre disponível no disco.

Neste exemplo, vamos classificar uma imagem pré-fornecida de um Panda. Execute este comando para executar o programa classificador de imagens:

  • python classify_image.py

Você receberá uma saída semelhante a esta:

Output
giant panda, panda, panda bear, coon bear, Ailuropoda melanoleuca (score = 0.89107) indri, indris, Indri indri, Indri brevicaudatus (score = 0.00779) lesser panda, red panda, panda, bear cat, cat bear, Ailurus fulgens (score = 0.00296) custard apple (score = 0.00147) earthstar (score = 0.00117)

Você classificou sua primeira imagem usando os recursos de reconhecimento de imagem do TensorFlow.

Se você quiser usar outra imagem, você pode fazer isso adicionando o argumento -- image_file ao seu comando python3 classify_image.py. Para o argumento, você passaria o caminho absoluto do arquivo de imagem.

Conclusão

Neste tutorial, você instalou o TensorFlow em um ambiente virtual do Python e validou o funcionamento do TensorFlow executando alguns exemplos. Agora você possui ferramentas que possibilitam a exploração de tópicos adicionais, incluindo Redes Neurais Convolucionais e Word Embeddings ou Vetores de Palavras.

O guia do programador do TensorFlow fornece um recurso útil e uma referência para o desenvolvimento do TensorFlow. Você também pode explorar o Kaggle, um ambiente competitivo para a aplicação prática de conceitos de machine learning que o colocam contra outros entusiastas de machine learning, ciência de dados e estatística. Eles têm um wiki robusto onde você pode explorar e compartilhar soluções, algumas das quais estão na vanguarda das técnicas estatísticas e de machine learning.

DigitalOcean Community Tutorials

Como Instalar o Java com `apt` no ubuntu-18-04

O autor selecionou o Open Internet/Free Speech Fund para receber uma doação de $ 100 como parte do programa Escreva para DOações.

Introdução

Java e a JVM (Java’s virtual machine) são necessários para utilizar vários tipos de software, incluindo o Tomcat, Jetty, Glassfish, Cassandra e Jenkins.

Neste guia, você irá instalar várias versões do Java Runtime Environment (JRE) e do Java Developer Kit (JDK) utilizando o apt. Você irá instalar o OpenJDK e também os pacotes oficiais da Oracle. Em seguida, você irá selecionar a versão que você deseja utilizar em seus projetos. Quando você finalizar o guia, você será capaz de utilizar o JDK para desenvolver seus programas ou utilizar o Java Runtime para rodar seus programas.

Pré-requisitos

Para seguir ester tutorial, você precisará de:

Instalando o JRE/JDK Padrão

A opção mais fácil para instalar o Java é utilizando o pacote que vem com o Ubuntu. Por padrão, o Ubuntu 18.04 inclui o OpenJDK, que é a alternativa open-source do JRE e JDK.

Esse pacote irá instalar ou o OpenJDK 10 ou o 11.

  • Antes de Setembro de 2018, ele irá instalar o OpenJDK 10.
  • Depois de Setembro de 2018, ele irá instalar o OpenJDK 11.

Para instalar essa versão, primeiro precisamos atualizar a lista de pacotes do apt:

  • sudo apt update

Depois, checar se o Java já está instalado:

  • java -version

Se o Java não estiver instalado, você verá a seguinte mensagem:

Output
Command 'java' not found, but can be installed with: apt install default-jre apt install openjdk-11-jre-headless apt install openjdk-8-jre-headless

Execute o seguinte comando para instalar o OpenJDK:

  • sudo apt install default-jre

Esse comando irá instalar o Java Runtime Environment (JRE). Isso vai permitir que você execute praticamente todos os programas em Java.

Verifique a instalação com:

  • java -version

Você verá a seguinte mensagem:

Output
openjdk version "10.0.2" 2018-07-17 OpenJDK Runtime Environment (build 10.0.2+13-Ubuntu-1ubuntu0.18.04.4) OpenJDK 64-Bit Server VM (build 10.0.2+13-Ubuntu-1ubuntu0.18.04.4, mixed mode)

Você talvez precise do Java Development Kit (JDK) junto do JRE para poder compilar e rodar algum programa específico em Java. Para instalar o JDK, execute os seguintes comandos, que também irão instalar o JRE:

  • sudo apt install default-jdk

Verifique se o JDK foi instalado checando a versão do javac, o compilador Java:

  • javac -version

Você verá a seguinte mensagem:

Output
javac 10.0.2

A seguir, veremos como especificar uma versão do OpenJDK que nós queremos instalar.

Instalando uma versão especifica do OpenJDK

OpenJDK 8

Java 8 é a versão Long Term Support (Suporte de longo prazo) atual e ainda é amplamente suportada, apesar da manutenção pública terminar em Janeiro de 2019. Para instalar o OpenJDK 8, execute o seguinte comando:

  • sudo apt install openjdk-8-jdk

Verifique se foi instalado com:

  • java -version

Você verá a seguinte mensagem:

Output
openjdk version "1.8.0_191" OpenJDK Runtime Environment (build 1.8.0_191-8u191-b12-2ubuntu0.18.04.1-b12) OpenJDK 64-Bit Server VM (build 25.191-b12, mixed mode)

Também é possível instalar somente o JRE, o que você pode fazer executando o seguinte comando sudo apt install openjdk-8-jre.

OpenJDK 10/11

Os repositórios do Ubuntu possuem os pacotes que instalarão o Java 10 ou o 11. Até Setembro de 2018, esse pacote irá instalar o OpenJDK 10. Assim que o Java 11 for lançado, esse pacote instalará o Java 11.

Para instalar o OpenJDK 10/10, execute o seguinte comando:

  • sudo apt install openjdk-11-jdk

Para instalar somente o JRE, use o seguinte comando:

  • sudo apt install openjdk-11-jre

A seguir, vamos ver como instalar o JDK e o JRE oficiais da Oracle.

Instalando o Oracle JDK

Se quiser instalar o Oracle JDK, que é a versão oficial distribuída pela Oracle, você precisará adicionar um novo repositório de pacotes para a versão que você gostaria de instalar.

Para instalar o Java 8, que é a última versão LTS, primeiramente adicione o repositório do pacote:

  • sudo add-apt-repository ppa:webupd8team/java

Quando você adicionar o repositório, você verá uma mensagem parecida com essa:

output
Oracle Java (JDK) Installer (automatically downloads and installs Oracle JDK8). There are no actual Java files in this PPA. Important -> Why Oracle Java 7 And 6 Installers No Longer Work: http://www.webupd8.org/2017/06/why-oracle-java-7-and-6-installers-no.html Update: Oracle Java 9 has reached end of life: http://www.oracle.com/technetwork/java/javase/downloads/jdk9-downloads-3848520.html The PPA supports Ubuntu 18.10, 18.04, 16.04, 14.04 and 12.04. More info (and Ubuntu installation instructions): - http://www.webupd8.org/2012/09/install-oracle-java-8-in-ubuntu-via-ppa.html Debian installation instructions: - Oracle Java 8: http://www.webupd8.org/2014/03/how-to-install-oracle-java-8-in-debian.html For Oracle Java 11, see a different PPA -> https://www.linuxuprising.com/2018/10/how-to-install-oracle-java-11-in-ubuntu.html More info: https://launchpad.net/~webupd8team/+archive/ubuntu/java Press [ENTER] to continue or Ctrl-c to cancel adding it.

Pressione ENTER para continuar. Depois atualize sua lista de pacotes:

  • sudo apt update

Quando a lista de pacotes atualizar, instale o Java 8:

  • sudo apt install oracle-java8-installer

Seu sistema irá realizar o download do JDK da Oracle e irá solicitar que você aceite os termos de licença. Aceite os termos e o JDK será instalado.

Agora vamos ver como selecionar qual versão do Java você deseja utilizar.

Gerenciando o Java

Você pode ter múltiplas instalações do Java em um servidor. Você pode configurar qual versão será utilizada por padrão no terminal, usando o comando update-alternatives.

  • sudo update-alternatives --config java

Será assim que a saída vai parecer se você instalou todas as versões de Java desse tutorial:

Output
There are 3 choices for the alternative java (providing /usr/bin/java). Selection Path Priority Status ------------------------------------------------------------ * 0 /usr/lib/jvm/java-11-openjdk-amd64/bin/java 1101 auto mode 1 /usr/lib/jvm/java-11-openjdk-amd64/bin/java 1101 manual mode 2 /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java 1081 manual mode 3 /usr/lib/jvm/java-8-oracle/jre/bin/java 1081 manual mode

Escolha o número que está associado com a versão do Java que será utilizada como padrão, ou pressione ENTER para deixar a configuração atual no lugar.

Você pode usar isso para outros comandos Java, como o compilador (javac):

  • sudo update-alternatives --config javac

Outros comandos para os quais esse comando pode ser utilizado incluem, mas não ficam limitados a: keytool, javadoc e jarsigner.

Configurando a Variavel de Ambiente JAVA_HOME

Muitos programas escritos em Java, utilizam a variável de ambiente JAVA_HOME para determinar o local de instalação do Java.

Para configurar esta variável de ambiente, primeiramente defina onde o Java está instalado. Utilize o comando update-alternatives:

  • sudo update-alternatives --config java

Esse comando mostra cada instalação do Java junto com seu caminho de instalação:

Output
There are 3 choices for the alternative java (providing /usr/bin/java). Selection Path Priority Status ------------------------------------------------------------ * 0 /usr/lib/jvm/java-11-openjdk-amd64/bin/java 1101 auto mode 1 /usr/lib/jvm/java-11-openjdk-amd64/bin/java 1101 manual mode 2 /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java 1081 manual mode 3 /usr/lib/jvm/java-8-oracle/jre/bin/java 1081 manual mode Press <enter> to keep the current choice[*], or type selection number:

Nesse caso, os caminhos de instalação são os seguintes:

  1. OpenJDK 11 está localizado em /usr/lib/jvm/java-11-openjdk-amd64/bin/java.
  2. OpenJDK 8 está localizado em /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java.
  3. Oracle Java 8 está localizado em /usr/lib/jvm/java-8-oracle/jre/bin/java.

Copie o caminho da instalação que você deseja utilizar. Depois abra /etc/environment utilizando o nano ou o seu editor de texto favorito:

  • sudo nano /etc/environment

No final desse arquivo, adicione a seguinte linha, certificando-se de substituir o caminho destacado com o que você copiou do seu sistema:

/etc/environment
JAVA_HOME="/usr/lib/jvm/java-11-openjdk-amd64/bin/" 

Ao modificar esse arquivo você irá configurar o caminho JAVA_HOME para todos os usuários do seu sistema.

Salve o arquivo e saia do editor de texto.

Agora recarregue arquivo para aplicar as mudanças para sua sessão atual:

  • source /etc/environment

Verifique se a sua variável de ambiente foi configurada:

  • echo $ JAVA_HOME

Você verá o caminho que você acabou de configurar:

Output
/usr/lib/jvm/java-11-openjdk-amd64/bin/

Os outros usuários precisaram executar o comando source /etc/environment ou desconectar e logar novamente para aplicar essa configuração.

Conclusão

Nesse tutorial você instalou múltiplas versões do Java e aprendeu como gerenciá-las. Você agora pode instalar os programas que rodam em Java, tais como o Tomcat, Jetty, Glassfish, Cassandra ou Jenkins.

DigitalOcean Community Tutorials

Automating Initial Server Setup with Ansible on Ubuntu 18.04

Introduction

When you first create a new Ubuntu 18.04 server, there are a few configuration steps that you should take early on as part of the basic setup. This will increase the security and usability of your server, working as a solid foundation for subsequent actions.

While you can complete these steps manually, automating the process will save you time and eliminate human error. With the popularization of containerized applications and microservices, server automation now plays an essential role in systems administration. It is also a way to establish standard procedures for new servers.

This guide explains how to use Ansible to automate the steps contained in our Initial Server Setup Guide. Ansible is a modern configuration management tool that can be used to automate the provisioning and configuration of remote systems.

Pre-Flight Check

In order to execute the automated setup provided by the playbook we’re discussing in this guide, you’ll need:

  • Ansible installed either on your local machine or on a remote server that you have set up as an Ansible Control Node. You can follow Step 1 of the tutorial How to Install and Configure Ansible on Ubuntu 18.04 to get this set up.
  • Root access to one or more Ubuntu 18.04 servers that will be managed by Ansible.

Before running a playbook, it’s important to make sure Ansible is able to connect to your servers via SSH and run Ansible modules using Python. The next two sections cover how to set up your Ansible inventory to include your servers and how to run ad-hoc Ansible commands to test for connectivity and valid credentials.

Inventory File

The inventory file contains information about the hosts you’ll manage with Ansible. You can include anywhere from one to several hundred of servers in your inventory file, and hosts can be organized into groups and subgroups. The inventory file is also often used to set variables that will be valid for certain hosts and groups only, in order to be used within playbooks and templates. Some variables can also affect the way a playbook is run, like the ansible_python_interpreter variable that we’ll see in a moment.

To inspect the contents of your default Ansible inventory, open the /etc/ansible/hosts file using your command-line editor of choice, on your local machine or an Ansible Control Node:

  • sudo nano /etc/ansible/hosts

Note: some Ansible installations won’t create a default inventory file. If the file doesn’t exist in your system, you can create a new file at /etc/ansible/hosts or provide a custom inventory path using the -i parameter when running commands and playbooks.

The default inventory file provided by the Ansible installation contains a number of examples that you can use as references for setting up your inventory. The following example defines a group named servers with three different servers in it, each identified by a custom alias: server1, server2, and server3:

/etc/ansible/hosts
[servers] server1 ansible_host=203.0.113.111 server2 ansible_host=203.0.113.112 server3 ansible_host=203.0.113.113  [servers:vars] ansible_python_interpreter=/usr/bin/python3 

The server:vars subgroup sets the ansible_python_interpreter host parameter that will be valid for all hosts included in the servers group. This parameter makes sure the remote server uses the /usr/bin/python3 Python 3 executable instead of /usr/bin/python (Python 2.7), which is not present on recent Ubuntu versions.

To finish setting up your inventory file, replace the highlighted IPs with the IP addresses of your servers. When you’re finished, save and close the file by pressing CTRL+X then y to confirm changes and then ENTER.

Now that your inventory file is ready, it’s time to test connectivity to your nodes

Testing Connectivity

After setting up the inventory file to include your servers, it’s time to check if Ansible is able to connect to these servers and run commands via SSH. For this guide, we will be using the Ubuntu root account because that’s typically the only account available by default on newly created servers. This playbook will create a new non-root user with sudo privileges that you should use in subsequent interactions with the remote server.

From your local machine or Ansible Control Node, run:

  • ansible -m ping all -u root

This command will use the built-in ping Ansible module to run a connectivity test on all nodes from your default inventory, connecting as root. The ping module will test:
if hosts are accessible;
if you have valid SSH credentials;
if hosts are able to run Ansible modules using Python.

If instead of key-based authentication you’re using password-based authentication to connect to remote servers, you should provide the additional parameter -k to the Ansible command, so that it will prompt you for the password of the connecting user.

  • ansible -m ping all -u root -k

Note: Keep in mind that some servers might have additional security measures against password-based authentication as the root user, and in some cases you might be required to manually log in to the server to change the initial root password.

You should get output similar to this:

Output
server1 | SUCCESS => { "changed": false, "ping": "pong" } server2 | SUCCESS => { "changed": false, "ping": "pong" } server3 | SUCCESS => { "changed": false, "ping": "pong" }

If this is the first time you’re connecting to these servers via SSH, you’ll be asked to confirm the authenticity of the hosts you’re connecting to via Ansible. When prompted, type yes and then hit Enter to confirm.

Once you get a “pong” reply back from a host, it means you’re ready to run Ansible commands and playbooks on that server.

What Does this Playbook Do?

This Ansible playbook provides an alternative to manually running through the procedure outlined in the Ubuntu 18.04 initial server setup guide and the guide on setting up SSH keys on Ubuntu 18.04.

Running this playbook will cause the following actions to be performed:

  1. The administrative group wheels is created and then configured for passwordless sudo.
  2. A new administrative user is created within that group, using the name specified by the create_user variable.
  3. A public SSH key is copied from the location defined by the variable copy_local_key, and added to the authorized_keys file for the user created in the previous step.
  4. Password-based authentication is disabled for the root user.
  5. The local apt package index is updated and basic packages defined by the variable sys_packages are installed.
  6. The UFW firewall is configured to allow only SSH connections and deny any other requests.

For more information about each of the steps included in this playbook, please refer to our Ubuntu 18.04 initial server setup guide.

Once the playbook has finished running, you’ll be able to log in to the server using the newly created sudo account.

How to Use this Playbook

To get started, we’ll download the contents of the playbook to your Ansible Control Node. This can be either your local machine, or a remote server where you have Ansible installed and your inventory set up.

For your convenience, the contents of the playbook are also included in a further section of this guide.

To download this playbook from the command-line, you can use curl:

  • curl -L https://raw.githubusercontent.com/do-community/ansible-playbooks/master/initial_server_setup/ubuntu1804.yml -o initial_server_setup.yml

This will download the contents of the playbook to a file named initial_server_setup.yml on your current local path. You can examine the contents of the playbook by opening the file with your command-line editor of choice:

  • sudo nano initial_server_setup.yml

Once you’ve opened the playbook file, you should notice a section named vars with three distinct variables that require your attention:

  • create_user: The name of the non-root user account to create and grant sudo privileges to. Our example uses sammy, but you can use whichever username you’d like.
  • copy_local_key: Local path to a valid SSH public key to set up as an authorized key for the new non-root sudo account. The default value points to the current local user’s public key located at ~/.ssh/id_rsa.pub.
  • sys_packages: A list of basic system packages that will be installed using the package manager tool apt.

Once you’re done updating the variables inside initial_server_setup.yml, save and close the file.

You’re now ready to run this playbook on one or more servers. Most playbooks are configured to be executed on all servers from your inventory, by default. We can use the -l flag to make sure that only a subset of servers, or a single server, is affected by the playbook. To execute the playbook only on server1, you can use the following command:

  • ansible-playbook initial_server_setup.yml -l server1

You will get output similar to this:

Output
PLAY [all] *************************************************************************************************************************************** TASK [Make sure we have a 'wheel' group] ********************************************************************************************************* changed: [server1] TASK [Allow 'wheel' group to have passwordless sudo] ********************************************************************************************* changed: [server1] TASK [Create a new regular user with sudo privileges] ******************************************************************************************** changed: [server1] TASK [Set authorized key for remote user] ******************************************************************************************************** changed: [server1] TASK [Disable password authentication for root] ************************************************************************************************** changed: [server1] TASK [Update apt] ******************************************************************************************************************************** changed: [server1] TASK [Install required system packages] ********************************************************************************************************** ok: [server1] TASK [UFW - Allow SSH connections] *************************************************************************************************************** changed: [server1] TASK [UFW - Deny all other incoming traffic by default] ****************************************************************************************** changed: [server1] PLAY RECAP *************************************************************************************************************************************** server1 : ok=9 changed=8 unreachable=0 failed=0

Once the playbook execution is finished, you’ll be able to log in to the server with:

  • ssh sammy@server_domain_or_IP

Remember to replace sammy with the user defined by the create_user variable, and server_domain_or_IP with your server’s hostname or IP address.

In case you have set a custom public key with the copy_local_key variable, you’ll need to provide an extra parameter specifying the location of its private key counterpart:

  • ssh sammy@server_domain_or_IP -i ~/.ssh/ansible_controller_key

After logging in to the server, you can check the UFW firewall’s active rules to confirm that it’s properly configured:

  • sudo ufw status

You should get output similar to this:

Output
Status: active To Action From -- ------ ---- OpenSSH ALLOW Anywhere OpenSSH (v6) ALLOW Anywhere (v6)

This means that the UFW firewall has successfully been enabled. Since this was the last task in the playbook, it confirms that the playbook was fully executed on this server.

The Playbook Contents

You can find the initial server setup playbook in the ansible-playbooks repository in the DigitalOcean Community GitHub organization. To copy or download the script contents directly, click the Raw button towards the top of the script, or click here to view the raw contents directly.

The full contents are also included here for convenience:

initial_server_setup.yml
--- - hosts: all   remote_user: root   gather_facts: false   vars:     create_user: sammy     copy_local_key: "{{ lookup('file', lookup('env','HOME') + '/.ssh/id_rsa.pub') }}"     sys_packages: [ 'curl', 'vim', 'git', 'ufw' ]    tasks:     - name: Make sure we have a 'wheel' group       group:         name: wheel         state: present      - name: Allow 'wheel' group to have passwordless sudo       lineinfile:         path: /etc/sudoers         state: present         regexp: '^%wheel'         line: '%wheel ALL=(ALL) NOPASSWD: ALL'         validate: '/usr/sbin/visudo -cf %s'      - name: Create a new regular user with sudo privileges       user:         name: "{{ create_user }}"         groups: wheel         shell: /bin/bash      - name: Set authorized key for remote user       authorized_key:         user: "{{ create_user }}"         state: present         key: "{{ copy_local_key }}"      - name: Disable password authentication for root       lineinfile:         path: /etc/ssh/sshd_config         state: present         regexp: '^PermitRootLogin'         line: 'PermitRootLogin prohibit-password'      - name: Update apt       apt: update_cache=yes      - name: Install required system packages       apt: name={{ sys_packages }} state=latest      - name: UFW - Allow SSH connections       ufw:         rule: allow         name: OpenSSH      - name: UFW - Deny all other incoming traffic by default       ufw:         state: enabled         policy: deny         direction: incoming  

Feel free to modify this playbook or include new tasks to best suit your individual needs within your own workflow.

Conclusion

Automating the initial server setup can save you time, while also making sure your servers will follow a standard configuration that can be improved and customized to your needs. With the distributed nature of modern applications and the need for more consistency between different staging environments, automation like this becomes a necessity.

In this guide, we demonstrated how to use Ansible for automating the initial tasks that should be executed on a fresh server, such as creating a non-root user with sudo access, enabling UFW and disabling remote root login.

If you’d like to include new tasks in this playbook to further customize your initial server setup, please refer to our introductory Ansible guide Configuration Management 101: Writing Ansible Playbooks.

DigitalOcean Community Tutorials