<?xml version="1.0" encoding="UTF-8"?>
<feed xml:lang="en-US" xmlns="http://www.w3.org/2005/Atom">
  <id>tag:www.digitalocean.com,2005:/community/tutorials/feed</id>
  <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials"/>
  <link rel="self" type="application/atom+xml" href="https://www.digitalocean.com/community/tutorials/feed"/>
  <title>DigitalOcean Community Tutorials</title>
  <updated>2018-10-19T17:24:14Z</updated>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/an-introduction-to-the-kubernetes-dns-service</id>
    <published>2018-10-17T20:22:37Z</published>
    <updated>2018-10-19T17:24:14Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/an-introduction-to-the-kubernetes-dns-service"/>
    <title>An Introduction to the Kubernetes DNS Service</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;The Domain Name System (DNS) is a system for associating various types of information – such as IP addresses – with easy-to-remember names. By default most Kubernetes clusters automatically configure an internal DNS service to provide a lightweight mechanism for service discovery. Built-in service discovery makes it easier for applications to find and communicate with each other on Kubernetes clusters, even when pods and services are being created, deleted, and shifted between nodes.&lt;/p&gt;

&lt;p&gt;The implementation details of the Kubernetes DNS service have changed in recent versions of Kubernetes. In this article we will take a look at both the &lt;strong&gt;kube-dns&lt;/strong&gt; and &lt;strong&gt;CoreDNS&lt;/strong&gt; versions of the Kubernetes DNS service. We will review how they operate and the DNS records that Kubernetes generates.&lt;/p&gt;

&lt;p&gt;To gain a more thorough understanding of DNS before you begin, please read &lt;a href="https://www.digitalocean.com/community/tutorials/an-introduction-to-dns-terminology-components-and-concepts"&gt;&lt;em&gt;An Introduction to DNS Terminology, Components, and Concepts&lt;/em&gt;&lt;/a&gt;. For any Kubernetes topics you may be unfamiliar with, you could read &lt;a href="https://www.digitalocean.com/community/tutorials/an-introduction-to-kubernetes"&gt;&lt;em&gt;An Introduction to Kubernetes&lt;/em&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id="what-does-the-kubernetes-dns-service-provide"&gt;What Does the Kubernetes DNS Service Provide?&lt;/h2&gt;

&lt;p&gt;Before Kubernetes version 1.11, the Kubernetes DNS service was based on &lt;strong&gt;kube-dns&lt;/strong&gt;. Version 1.11 introduced &lt;strong&gt;CoreDNS&lt;/strong&gt; to address some security and stability concerns with kube-dns.&lt;/p&gt;

&lt;p&gt;Regardless of the software handling the actual DNS records, both implementations work in a similar manner:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  A service named &lt;code&gt;kube-dns&lt;/code&gt; and one or more pods are created.&lt;/li&gt;
&lt;li&gt;  The &lt;code&gt;kube-dns&lt;/code&gt; service listens for &lt;strong&gt;service&lt;/strong&gt; and &lt;strong&gt;endpoint&lt;/strong&gt; events from the Kubernetes API and updates its DNS records as needed. These events are triggered when you create, update or delete Kubernetes services and their associated pods.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;kubelet sets each new pod's &lt;code&gt;/etc/resolv.conf&lt;/code&gt; &lt;code&gt;nameserver&lt;/code&gt; option to the cluster IP of the &lt;code&gt;kube-dns&lt;/code&gt; service, with appropriate &lt;code&gt;search&lt;/code&gt; options to allow for shorter hostnames to be used:&lt;/p&gt;
&lt;div class="code-label " title="resolv.conf"&gt;resolv.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;nameserver 10.32.0.10
search namespace.svc.cluster.local svc.cluster.local cluster.local
options ndots:5
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Applications running in containers can then resolve hostnames such as &lt;code&gt;example-service.namespace&lt;/code&gt; into the correct cluster IP addresses.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id="example-kubernetes-dns-records"&gt;Example Kubernetes DNS Records&lt;/h3&gt;

&lt;p&gt;The full DNS &lt;code&gt;A&lt;/code&gt; record of a Kubernetes service will look like the following example:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;span class="highlight"&gt;service&lt;/span&gt;.&lt;span class="highlight"&gt;namespace&lt;/span&gt;.svc.cluster.local
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A pod would have a record in this format, reflecting the actual IP address of the pod:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;span class="highlight"&gt;10.32.0.125&lt;/span&gt;.&lt;span class="highlight"&gt;namespace&lt;/span&gt;.pod.cluster.local
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Additionally, &lt;code&gt;SRV&lt;/code&gt; records are created for a Kubernetes service's named ports:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;_&lt;span class="highlight"&gt;port-name&lt;/span&gt;._&lt;span class="highlight"&gt;protocol&lt;/span&gt;.&lt;span class="highlight"&gt;service&lt;/span&gt;.&lt;span class="highlight"&gt;namespace&lt;/span&gt;.svc.cluster.local
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The result of all this is a built-in, DNS-based service discovery mechanism, where your application or microservice can target a simple and consistent hostname to access other services or pods on the cluster.&lt;/p&gt;

&lt;h3 id="search-domains-and-resolving-shorter-hostnames"&gt;Search Domains and Resolving Shorter Hostnames&lt;/h3&gt;

&lt;p&gt;Because of the search domain suffixes listed in the &lt;code&gt;resolv.conf&lt;/code&gt; file, you often won't need to use the full hostname to contact another service. If you're addressing a service in the same namespace, you can use just the service name to contact it:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;span class="highlight"&gt;other-service&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If the service is in a different namespace, add it to the query:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;span class="highlight"&gt;other-service&lt;/span&gt;.&lt;span class="highlight"&gt;other-namespace&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you're targeting a pod, you'll need to use at least the following:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;span class="highlight"&gt;pod-ip&lt;/span&gt;.&lt;span class="highlight"&gt;other-namespace&lt;/span&gt;.pod
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As we saw in the default &lt;code&gt;resolv.conf&lt;/code&gt; file, only &lt;code&gt;.svc&lt;/code&gt; suffixes are automatically completed, so make sure you specify everything up to &lt;code&gt;.pod&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now that we know the practical uses of the Kubernetes DNS service, let's run through some details on the two different implementations.&lt;/p&gt;

&lt;h2 id="kubernetes-dns-implementation-details"&gt;Kubernetes DNS Implementation Details&lt;/h2&gt;

&lt;p&gt;As noted in the previous section, Kubernetes version 1.11 introduced new software to handle the &lt;code&gt;kube-dns&lt;/code&gt; service. The motivation for the change was to increase the performance and security of the service. Let's take a look at the original &lt;code&gt;kube-dns&lt;/code&gt; implementation first.&lt;/p&gt;

&lt;h3 id="kube-dns"&gt;kube-dns&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;kube-dns&lt;/code&gt; service prior to Kubernetes 1.11 is made up of three containers running in a &lt;code&gt;kube-dns&lt;/code&gt; pod in the &lt;code&gt;kube-system&lt;/code&gt; namespace. The three containers are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;kube-dns:&lt;/strong&gt; a container that runs &lt;a href="https://github.com/skynetservices/skydns"&gt;SkyDNS&lt;/a&gt;, which performs DNS query resolution&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;dnsmasq:&lt;/strong&gt; a popular lightweight DNS resolver and cache that caches the responses from SkyDNS&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;sidecar:&lt;/strong&gt; a sidecar container that handles metrics reporting and responds to health checks for the service&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Security vulnerabilities in Dnsmasq, and scaling performance issues with SkyDNS led to the creation of a replacement system, CoreDNS.&lt;/p&gt;

&lt;h3 id="coredns"&gt;CoreDNS&lt;/h3&gt;

&lt;p&gt;As of Kubernetes 1.11 a new Kubernetes DNS service, &lt;strong&gt;CoreDNS&lt;/strong&gt; has been promoted to General Availability. This means that it's ready for production use and will be the default cluster DNS service for many installation tools and managed Kubernetes providers.&lt;/p&gt;

&lt;p&gt;CoreDNS is a single process, written in Go, that covers all of the functionality of the previous system. A single container resolves and caches DNS queries, responds to health checks, and provides metrics.&lt;/p&gt;

&lt;p&gt;In addition to addressing performance- and security-related issues, CoreDNS fixes some other minor bugs and adds some new features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Some issues with incompatibilities between using stubDomains and external services have been fixed&lt;/li&gt;
&lt;li&gt;  CoreDNS can enhance DNS-based round-robin load balancing by randomizing the order in which it returns certain records&lt;/li&gt;
&lt;li&gt;  A feature called &lt;code&gt;autopath&lt;/code&gt; can improve DNS response times when resolving external hostnames, by being smarter about iterating through each of the search domain suffixes listed in &lt;code&gt;resolv.conf&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;  With kube-dns &lt;code&gt;10.32.0.125.namespace.pod.cluster.local&lt;/code&gt; would always resolve to &lt;code&gt;10.32.0.125&lt;/code&gt;, even if the pod doesn't actually exist. CoreDNS has a "pods verified" mode that will only resolve successfully if a pod exists with the right IP and in the right namespace.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For more information on CoreDNS and how it differs from kube-dns, you can read &lt;a href="https://kubernetes.io/blog/2018/07/10/coredns-ga-for-kubernetes-cluster-dns/"&gt;the Kubernetes CoreDNS GA announcement&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id="additional-configuration-options"&gt;Additional Configuration Options&lt;/h2&gt;

&lt;p&gt;Kubernetes operators often want to customize how their pods and containers resolve certain custom domains, or need to adjust the upstream nameservers or search domain suffixes configured in &lt;code&gt;resolv.conf&lt;/code&gt;. You can do this with the &lt;code&gt;dnsConfig&lt;/code&gt; option of your pod's spec:&lt;/p&gt;
&lt;div class="code-label " title="example_pod.yaml"&gt;example_pod.yaml&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;apiVersion: v1
kind: Pod
metadata:
  namespace: example
  name: custom-dns
spec:
  containers:
    - name: example
      image: nginx
  dnsPolicy: "None"
  &lt;span class="highlight"&gt;dnsConfig:&lt;/span&gt;
    &lt;span class="highlight"&gt;nameservers:&lt;/span&gt;
      &lt;span class="highlight"&gt;- 203.0.113.44&lt;/span&gt;
    &lt;span class="highlight"&gt;searches:&lt;/span&gt;
      &lt;span class="highlight"&gt;- custom.dns.local&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Updating this config will rewrite a pod's &lt;code&gt;resolv.conf&lt;/code&gt; to enable the changes. The configuration maps directly to the standard &lt;code&gt;resolv.conf&lt;/code&gt; options, so the above config would create a file with &lt;code&gt;nameserver 203.0.113.44&lt;/code&gt; and &lt;code&gt;search custom.dns.local&lt;/code&gt; lines.&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;In this article we covered the basics of what the Kubernetes DNS service provides to developers, showed some example DNS records for services and pods, discussed how the system is implemented on different Kubernetes versions, and highlighted some additional configuration options available to customize how your pods resolve DNS queries.&lt;/p&gt;

&lt;p&gt;For more information on the Kubernetes DNS service, please refer to &lt;a href="https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/"&gt;the official Kubernetes &lt;em&gt;DNS for Services and Pods&lt;/em&gt; documentation&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-install-configure-pgadmin4-server-mode</id>
    <published>2018-10-19T14:21:46Z</published>
    <updated>2018-10-19T15:09:50Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-install-configure-pgadmin4-server-mode"/>
    <title>How to Install and Configure pgAdmin 4 in Server Mode</title>
    <content type="html">&lt;h2 id="introduction"&gt;Introduction&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.pgadmin.org/"&gt;pgAdmin&lt;/a&gt; is an open-source administration and development platform for PostgreSQL and its related database management systems. Written in Python and jQuery, it supports all the features found in PostgreSQL. You can use pgAdmin to do everything from writing basic SQL queries to monitoring your databases and configuring advanced database architectures.&lt;/p&gt;

&lt;p&gt;In this tutorial, we'll walk through the process of installing and configuring the latest version of pgAdmin onto an Ubuntu 18.04 server, accessing pgAdmin through a web browser, and connecting it to a PostgreSQL database on your server.&lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;To complete this tutorial, you will need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A server running Ubuntu 18.04. This server should have a non-root user with sudo privileges, as well as a firewall configured with &lt;code&gt;ufw&lt;/code&gt;. For help with setting this up, follow our &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-18-04"&gt;Initial Server Setup Guide for Ubuntu 18.04&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The Apache web server installed on your server. Follow our guide on &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-the-apache-web-server-on-ubuntu-18-04"&gt;How To Install the Apache Web Server on Ubuntu 18.04&lt;/a&gt; to configure this on your machine.&lt;/li&gt;
&lt;li&gt;PostgreSQL installed on your server. You can set this up by following our guide on &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-and-use-postgresql-on-ubuntu-18-04"&gt;How To Install and Use PostgreSQL on Ubuntu 18.04&lt;/a&gt;. As you follow this guide, &lt;strong&gt;be sure to create a new role and database&lt;/strong&gt;, as you will need both to connect pgAdmin to your PostgreSQL instance.&lt;/li&gt;
&lt;li&gt;Python 3 and &lt;code&gt;venv&lt;/code&gt; installed on your server. Follow &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-python-3-and-set-up-a-programming-environment-on-an-ubuntu-18-04-server"&gt;How To Install Python 3 and Set Up a Programming Environment on an Ubuntu 18.04 server&lt;/a&gt; to install these tools and set up a virtual environment.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="step-1-—-installing-pgadmin-and-its-dependencies"&gt;Step 1 — Installing pgAdmin and its Dependencies&lt;/h2&gt;

&lt;p&gt;As of this writing, the most recent version of pgAdmin is pgAdmin 4, while the most recent version available through the official Ubuntu repositories is pgAdmin 3. pgAdmin 3 is no longer supported though, and the project maintainers recommend installing pgAdmin 4. In this step, we will go over the process of installing the latest version of pgAdmin 4 within a virtual environment (as recommended by the project's development team) and installing its dependencies using &lt;code&gt;apt&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;To begin, update your server’s package index if you haven't done so recently:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt update
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, install the following dependencies. These include &lt;code&gt;libgmp3-dev&lt;/code&gt;, a multiprecision arithmetic library; &lt;code&gt;libpq-dev&lt;/code&gt;, which includes header files and a static library that helps communication with a PostgreSQL backend; and &lt;code&gt;libapache2-mod-wsgi-py3&lt;/code&gt;, an Apache module that allows you to host Python-based web applications within Apache:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install libgmp3-dev libpq-dev libapache2-mod-wsgi-py3
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Following this, create a few directories where pgAdmin will store its sessions data, storage data, and logs:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo mkdir -p /var/lib/pgadmin4/sessions
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo mkdir /var/lib/pgadmin4/storage
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo mkdir /var/log/pgadmin4
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, change ownership of these directories to your non-root user and group. This is necessary because they are currently owned by your &lt;strong&gt;root&lt;/strong&gt; user, but we will install pgAdmin from a virtual environment owned by your non-root user, and the installation process involves creating some files within these directories. After the installation, however, we will change the ownership over to the &lt;strong&gt;www-data&lt;/strong&gt; user and group so it can be served to the web:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo chown -R &lt;span class="highlight"&gt;sammy&lt;/span&gt;:&lt;span class="highlight"&gt;sammy&lt;/span&gt; /var/lib/pgadmin4
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo chown -R &lt;span class="highlight"&gt;sammy&lt;/span&gt;:&lt;span class="highlight"&gt;sammy&lt;/span&gt; /var/log/pgadmin4
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, open up your virtual environment. Navigate to the directory your programming environment is in and activate it. Following the naming conventions of the &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-python-3-and-set-up-a-programming-environment-on-an-ubuntu-18-04-server"&gt;prerequisite Python 3 tutorial&lt;/a&gt;, we’ll go to the &lt;code&gt;environments&lt;/code&gt; directory and activate the &lt;code&gt;my_env&lt;/code&gt; environment:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cd environments/
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;source &lt;span class="highlight"&gt;my_env&lt;/span&gt;/bin/activate
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Following this, download the pgAdmin 4 source code onto your machine. To find the latest version of the source code, navigate to the &lt;a href="https://www.pgadmin.org/download/pgadmin-4-python-wheel/"&gt;pgAdmin 4 (Python Wheel) Download page&lt;/a&gt; and click the link for the latest version (v3.4, as of this writing). This will take you to a &lt;strong&gt;Downloads&lt;/strong&gt; page on the PostgreSQL website. Once there, copy the file link that ends with &lt;code&gt;.whl&lt;/code&gt; — the standard built-package format used for Python distributions. Then go back to your terminal and run the following &lt;code&gt;wget&lt;/code&gt; command, making sure to replace the link with the one you copied from the PostgreSQL site, which will download the &lt;code&gt;.whl&lt;/code&gt; file to your server:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="(my_env) sammy@server:~$"&gt;wget https://ftp.postgresql.org/pub/pgadmin/pgadmin4/v&lt;span class="highlight"&gt;3.4&lt;/span&gt;/pip/pgadmin4-&lt;span class="highlight"&gt;3.4&lt;/span&gt;-py2.py3-none-any.whl
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next install the &lt;code&gt;wheel&lt;/code&gt; package, the reference implementation of the wheel packaging standard. A Python library, this package serves as an extension for building wheels and includes a command line tool for working with &lt;code&gt;.whl&lt;/code&gt; files:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="(my_env) sammy@server:~$"&gt;python -m pip install wheel
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then install pgAdmin 4 package with the following command:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="(my_env) sammy@server:~$"&gt;python -m pip install pgadmin4-&lt;span class="highlight"&gt;3.4&lt;/span&gt;-py2.py3-none-any.whl 
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That takes care of installing pgAdmin and its dependencies. Before connecting it to your database, though, there are a few changes you’ll need to make to the program’s configuration.&lt;/p&gt;

&lt;h2 id="step-2-—-configuring-pgadmin-4"&gt;Step 2 — Configuring pgAdmin 4&lt;/h2&gt;

&lt;p&gt;Although pgAdmin has been installed on your server, there are still a few steps you must go through to ensure it has the permissions and configurations needed to allow it to correctly serve the web interface.&lt;/p&gt;

&lt;p&gt;pgAdmin’s main configuration file, &lt;code&gt;config.py&lt;/code&gt;, is read before any other configuration file. Its contents can be used as a reference point for further configuration settings that can be specified in pgAdmin’s other config files, but to avoid unforeseen errors, you should not edit the &lt;code&gt;config.py&lt;/code&gt; file itself. We will add some configuration changes to a new file, named &lt;code&gt;config_local.py&lt;/code&gt;, which will be read after the primary one.&lt;/p&gt;

&lt;p&gt;Create this file now using your preferred text editor. Here, we will use &lt;code&gt;nano&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="(my_env) sammy@server:~$"&gt;nano &lt;span class="highlight"&gt;my_env&lt;/span&gt;/lib/python3.6/site-packages/pgadmin4/config_local.py
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In your editor, add the following content:&lt;/p&gt;
&lt;div class="code-label " title="environments/my_env/lib/python3.6/site-packages/pgadmin4/config_local.py"&gt;environments/my_env/lib/python3.6/site-packages/pgadmin4/config_local.py&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;LOG_FILE = '/var/log/pgadmin4/pgadmin4.log'
SQLITE_PATH = '/var/lib/pgadmin4/pgadmin4.db'
SESSION_DB_PATH = '/var/lib/pgadmin4/sessions'
STORAGE_DIR = '/var/lib/pgadmin4/storage'
SERVER_MODE = True
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here are what these five directives do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;LOG_FILE&lt;/code&gt;: this defines the file in which pgAdmin’s logs will be stored. &lt;/li&gt;
&lt;li&gt;&lt;code&gt;SQLITE_PATH&lt;/code&gt;: pgAdmin stores user-related data in an SQLite database, and this directive points the pgAdmin software to this configuration database. Because this file is located under the persistent directory &lt;code&gt;/var/lib/pgadmin4/&lt;/code&gt;, your user data will not be lost after you upgrade.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SESSION_DB_PATH&lt;/code&gt;: specifies which directory will be used to store session data.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;STORAGE_DIR&lt;/code&gt;: defines where pgAdmin will store other data, like backups and security certificates.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SERVER_MODE&lt;/code&gt;: setting this directive to &lt;code&gt;True&lt;/code&gt; tells pgAdmin to run in Server mode, as opposed to Desktop mode.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Notice that each of these file paths point to the directories you created in Step 1.&lt;/p&gt;

&lt;p&gt;After adding these lines, save and close the file (press &lt;code&gt;CTRL + X&lt;/code&gt;, followed by &lt;code&gt;Y&lt;/code&gt; and then &lt;code&gt;ENTER&lt;/code&gt;). With those configurations in place, run the pgAdmin setup script to set your login credentials:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="(my_env) sammy@server:~$"&gt;python my_env/lib/python3.6/site-packages/pgadmin4/setup.py
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After running this command, you will see a prompt asking for your email address and a password. These will serve as your login credentials when you access pgAdmin later on, so be sure to remember or take note of what you enter here:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;. . .
Enter the email address and password to use for the initial pgAdmin user account:

Email address: &lt;span class="highlight"&gt;sammy@example.com&lt;/span&gt;
Password: 
Retype password:
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Following this, deactivate your virtual environment:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="(my_env) sammy@server:~$"&gt;deactivate 
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Recall the file paths you specified in the &lt;code&gt;config_local.py&lt;/code&gt; file. These files are held within the directories you created in Step 1, which are currently owned by your non-root user. They must, however, be accessible by the user and group running your web server. By default on Ubuntu 18.04, these are the &lt;strong&gt;www-data&lt;/strong&gt; user and group, so update the permissions on the following directories to give &lt;strong&gt;www-data&lt;/strong&gt; ownership over both of them:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo chown -R www-data:www-data /var/lib/pgadmin4/
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo chown -R www-data:www-data /var/log/pgadmin4/
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With that, pgAdmin is fully configured. However, the program isn't yet being served from your server, so it remains inaccessible. To resolve this, we will configure Apache to serve pgAdmin so you can access its user interface through a web browser.&lt;/p&gt;

&lt;h2 id="step-3-—-configuring-apache"&gt;Step 3 — Configuring Apache&lt;/h2&gt;

&lt;p&gt;The Apache web server uses &lt;em&gt;virtual hosts&lt;/em&gt; to encapsulate configuration details and host more than one domain from a single server. If you followed the prerequisite Apache tutorial, you may have set up an example virtual host file under the name &lt;code&gt;example.com.conf&lt;/code&gt;, but in this step we will create a new one from which we can serve the pgAdmin web interface. &lt;/p&gt;

&lt;p&gt;To begin, make sure you're in your root directory:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cd /
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then create a new file in your &lt;code&gt;/sites-available/&lt;/code&gt; directory called &lt;code&gt;pgadmin4.conf&lt;/code&gt;. This will be your server’s virtual host file:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/apache2/sites-available/pgadmin4.conf
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add the following content to this file, being sure to update the highlighted parts to align with your own configuration:&lt;/p&gt;
&lt;div class="code-label " title="/etc/apache2/sites-available/pgadmin4.conf"&gt;/etc/apache2/sites-available/pgadmin4.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&amp;lt;VirtualHost *&amp;gt;
    ServerName &lt;span class="highlight"&gt;your_server_ip&lt;/span&gt;

    WSGIDaemonProcess pgadmin processes=1 threads=25 python-home=/home/&lt;span class="highlight"&gt;sammy&lt;/span&gt;/&lt;span class="highlight"&gt;environments&lt;/span&gt;/&lt;span class="highlight"&gt;my_env&lt;/span&gt;
    WSGIScriptAlias / /home/&lt;span class="highlight"&gt;sammy&lt;/span&gt;/&lt;span class="highlight"&gt;environments&lt;/span&gt;/&lt;span class="highlight"&gt;my_env&lt;/span&gt;/lib/python3.6/site-packages/pgadmin4/pgAdmin4.wsgi

    &amp;lt;Directory "/home/&lt;span class="highlight"&gt;sammy&lt;/span&gt;/&lt;span class="highlight"&gt;environments&lt;/span&gt;/&lt;span class="highlight"&gt;my_env&lt;/span&gt;/lib/python3.6/site-packages/pgadmin4/"&amp;gt;
        WSGIProcessGroup pgadmin
        WSGIApplicationGroup %{GLOBAL}
        Require all granted
    &amp;lt;/Directory&amp;gt;
&amp;lt;/VirtualHost&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save and close the virtual host file. Next, use the &lt;code&gt;a2dissite&lt;/code&gt; script to disable the default virtual host file, &lt;code&gt;000-default.conf&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo a2dissite 000-default.conf
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;span class='note'&gt;&lt;p&gt;
&lt;strong&gt;Note:&lt;/strong&gt; If you followed the prerequisite Apache tutorial, you may have already disabled &lt;code&gt;000-default.conf&lt;/code&gt; and set up an example virtual host configuration file (named &lt;code&gt;example.com.conf&lt;/code&gt; in the prerequisite). If this is the case, you will need to disable the &lt;code&gt;example.com.conf&lt;/code&gt; virtual host file with the following command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo a2dissite example.com.conf
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;/p&gt;&lt;/span&gt;

&lt;p&gt;Then use the &lt;code&gt;a2ensite&lt;/code&gt; script to enable your &lt;code&gt;pgadmin4.conf&lt;/code&gt; virtual host file. This will create a symbolic link from the virtual host file in the &lt;code&gt;/sites-available/&lt;/code&gt; directory to the &lt;code&gt;/sites-enabled/&lt;/code&gt; directory:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo a2ensite pgadmin4.conf
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Following this, test that your configuration file’s syntax is correct:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;apachectl configtest
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If your configuration file is all in order, you will see &lt;code&gt;Syntax OK&lt;/code&gt;. If you see an error in the output, reopen the &lt;code&gt;pgadmin4.conf&lt;/code&gt; file and double check that your IP address and file paths are all correct, then rerun the &lt;code&gt;configtest&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Once you see &lt;code&gt;Syntax OK&lt;/code&gt; in your output, restart the Apache service so it reads your new virtual host file:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl restart apache2
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;pgAdmin is now fully installed and configured. Next, we'll go over how to access pgAdmin from a browser before connecting it to your PostgreSQL database. &lt;/p&gt;

&lt;h2 id="step-4-—-accessing-pgadmin"&gt;Step 4 — Accessing pgAdmin&lt;/h2&gt;

&lt;p&gt;On your local machine, open up your preferred web browser and navigate to your server’s IP address:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;http://&lt;span class="highlight"&gt;your_server_ip&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once there, you’ll be presented with a login screen similar to the following:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/pgadmin/pgadmin_login_blank.png" alt="pgAdmin login screen"&gt;&lt;/p&gt;

&lt;p&gt;Enter the login credentials you defined in Step 2, and you’ll be taken to the pgAdmin Welcome Screen:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/pgadmin/pgadmin_welcome_page_1.png" alt="pgAdmin Welcome Page"&gt;&lt;/p&gt;

&lt;p&gt;Now that you've confirmed you can access the pgAdmin interface, all that's left to do is to connect pgAdmin to your PostgreSQL database. Before doing so, though, you'll need to make one minor change to your PostgreSQL superuser's configuration.&lt;/p&gt;

&lt;h2 id="step-5-—-configuring-your-postgresql-user"&gt;Step 5 — Configuring your PostgreSQL User&lt;/h2&gt;

&lt;p&gt;If you followed the &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-and-use-postgresql-on-ubuntu-18-04"&gt;prerequisite PostgreSQL tutorial&lt;/a&gt;, you should already have PostgreSQL installed on your server with a new superuser role and database set up.&lt;/p&gt;

&lt;p&gt;By default in PostgreSQL, you authenticate as database users using the "Identification Protocol," or "ident," authentication method. This involves PostgreSQL taking the client's Ubuntu username and using it as the allowed database username. This can allow for greater security in many cases, but it can also cause issues in instances where you'd like an outside program, such as pgAdmin, to connect to one of your databases. To resolve this, we will set a password for this PostgreSQL role which will allow pgAdmin to connect to your database.&lt;/p&gt;

&lt;p&gt;From your terminal, open the PostgreSQL prompt under your superuser role:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo -u &lt;span class="highlight"&gt;sammy&lt;/span&gt; psql
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;From the PostgreSQL prompt, update the user profile to have a strong password of your choosing:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="sammy=#"&gt;ALTER USER &lt;span class="highlight"&gt;sammy&lt;/span&gt; PASSWORD '&lt;span class="highlight"&gt;password&lt;/span&gt;';
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then exit the PostgreSQL prompt:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="sammy=#"&gt;\q
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, go back to the pgAdmin 4 interface in your browser, and locate the &lt;strong&gt;Browser&lt;/strong&gt; menu on the left hand side. Right-click on &lt;strong&gt;Servers&lt;/strong&gt; to open a context menu, hover your mouse over &lt;strong&gt;Create&lt;/strong&gt;, and click &lt;strong&gt;Server…&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/pgadmin/create_server_box_resized.png" alt="Create Server context menu"&gt;&lt;/p&gt;

&lt;p&gt;This will cause a window to pop up in your browser in which you'll enter info about your server, role, and database.&lt;/p&gt;

&lt;p&gt;In the &lt;strong&gt;General&lt;/strong&gt; tab, enter the name for this server. This can be anything you'd like, but you may find it helpful to make it something descriptive. In our example, the server is named &lt;code&gt;Sammy-server-1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/pgadmin/server_general_tab_resized.png" alt="Create Server - General tab"&gt;&lt;/p&gt;

&lt;p&gt;Next, click on the &lt;strong&gt;Connection&lt;/strong&gt; tab. In the &lt;strong&gt;Host name/address&lt;/strong&gt; field, enter &lt;code&gt;localhost&lt;/code&gt;. The &lt;strong&gt;Port&lt;/strong&gt; should be set to &lt;code&gt;5432&lt;/code&gt; by default, which will work for this setup, as that's the default port used by PostgreSQL. &lt;/p&gt;

&lt;p&gt;In the &lt;strong&gt;Maintenance database&lt;/strong&gt; field, enter the name of the database you'd like to connect to. Note that this database must already be created on your server. Then, enter the PostgreSQL username and password you configured previously in the &lt;strong&gt;Username&lt;/strong&gt; and &lt;strong&gt;Password&lt;/strong&gt; fields, respectively.&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/pgadmin/connection_tab_resized.png" alt="Create Server - Connection tab"&gt;&lt;/p&gt;

&lt;p&gt;The empty fields in the other tabs are optional, and it's only necessary that you fill them in if you have a specific setup in mind in which they're required. Click the &lt;strong&gt;Save&lt;/strong&gt; button, and the database will appear under the &lt;strong&gt;Servers&lt;/strong&gt; in the &lt;strong&gt;Browser&lt;/strong&gt; menu.&lt;/p&gt;

&lt;p&gt;You've successfully connected pgAdmin4 to your PostgreSQL database. You can do just about anything from the pgAdmin dashboard that you would from the PostgreSQL prompt. To illustrate this, we will create an example table and populate it with some sample data through the web interface.&lt;/p&gt;

&lt;h2 id="step-6-—-creating-a-table-in-the-pgadmin-dashboard"&gt;Step 6 — Creating a Table in the pgAdmin Dashboard&lt;/h2&gt;

&lt;p&gt;From the pgAdmin dashboard, locate the &lt;strong&gt;Browser&lt;/strong&gt; menu on the left-hand side of the window. Click on the plus sign (&lt;strong&gt;+&lt;/strong&gt;) next to &lt;strong&gt;Servers (1)&lt;/strong&gt; to expand the tree menu within it. Next, click the plus sign to the left of the server you added in the previous step (&lt;strong&gt;Sammy-server-1&lt;/strong&gt; in our example), then expand &lt;strong&gt;Databases&lt;/strong&gt;, the name of the database you added (&lt;strong&gt;sammy&lt;/strong&gt;, in our example), and then &lt;strong&gt;Schemas (1)&lt;/strong&gt;. You should see a tree menu like the following:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/pgadmin/table_tree_menu_resized.png" alt="Expanded Browser tree menu"&gt;&lt;/p&gt;

&lt;p&gt;Right-click the &lt;strong&gt;Tables&lt;/strong&gt; list item, then hover your cursor over &lt;strong&gt;Create&lt;/strong&gt; and click &lt;strong&gt;Table…&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/pgadmin/create_table_resized.png" alt="Create Table context menu"&gt;&lt;/p&gt;

&lt;p&gt;This will open up a &lt;strong&gt;Create-Table&lt;/strong&gt; window. Under the &lt;strong&gt;General&lt;/strong&gt; tab of this window, enter a name for the table. This can be anything you'd like, but to keep things simple we'll refer to it as &lt;strong&gt;table-01&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/pgadmin/create_table_general_tab_1.png" alt="Create Table - General tab"&gt;&lt;/p&gt;

&lt;p&gt;Then navigate to the &lt;strong&gt;Columns&lt;/strong&gt; tab and click the &lt;strong&gt;+&lt;/strong&gt; sign in the upper right corner of the window to add some columns. When adding a column, you're required to give it a &lt;strong&gt;Name&lt;/strong&gt; and a &lt;strong&gt;Data type&lt;/strong&gt;, and you may need to choose a &lt;strong&gt;Length&lt;/strong&gt; if it's required by the data type you've selected.&lt;/p&gt;

&lt;p&gt;Additionally, the &lt;a href="https://www.postgresql.org/docs/9.1/static/ddl-constraints.html#AEN2520"&gt;official PostgreSQL documentation&lt;/a&gt; states that adding a primary key to a table is usually best practice. A &lt;em&gt;primary key&lt;/em&gt; is a constraint that indicates a specific column or set of columns that can be used as a special identifier for rows in the table. This isn't a requirement, but if you'd like to set one or more of your columns as the primary key, toggle the switch at the far right from &lt;strong&gt;No&lt;/strong&gt; to &lt;strong&gt;Yes&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Click the &lt;strong&gt;Save&lt;/strong&gt; button to create the table.&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/pgadmin/create_table_add_column_1primkey.png" alt="Create Table - Columns Tab with Primary Key turned on"&gt;&lt;/p&gt;

&lt;p&gt;By this point, you've created a table and added a couple columns to it. However, the columns don't yet contain any data. To add data to your new table, right-click the name of the table in the &lt;strong&gt;Browser&lt;/strong&gt; menu, hover your cursor over &lt;strong&gt;Scripts&lt;/strong&gt; and click on &lt;strong&gt;INSERT Script&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/pgadmin/insert_script_context_menu.png" alt="INSERT script context menu"&gt;&lt;/p&gt;

&lt;p&gt;This will open a new panel on the dashboard. At the top you'll see a partially-completed &lt;code&gt;INSERT&lt;/code&gt; statement, with the appropriate table and column names. Go ahead and replace the question marks (&lt;strong&gt;?&lt;/strong&gt;) with some dummy data, being sure that the data you add aligns with the data types you selected for each column. Note that you can also add multiple rows of data by adding each row in a new set of parentheses, with each set of parentheses separated by a comma as shown in the following example.&lt;/p&gt;

&lt;p&gt;If you'd like, feel free to replace the partially-completed &lt;code&gt;INSERT&lt;/code&gt; script with this example &lt;code&gt;INSERT&lt;/code&gt; statement:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;INSERT INTO public."table-01"(
    col1, col2, col3)
    VALUES ('Juneau', 14, 337), ('Bismark', 90, 2334), ('Lansing', 51, 556);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/pgadmin/insert_script.png" alt="Example INSERT statement"&gt;&lt;/p&gt;

&lt;p&gt;Click on the lightning bolt icon (&lt;strong&gt;⚡&lt;/strong&gt;) to execute the &lt;code&gt;INSERT&lt;/code&gt; statement. To view the table and all the data within it, right-click the name of your table in the &lt;strong&gt;Browser&lt;/strong&gt; menu once again, hover your cursor over &lt;strong&gt;View/Edit Data&lt;/strong&gt;, and select &lt;strong&gt;All Rows&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/pgadmin/view_edit_data_all_rows.png" alt="View/Edit Data, All Rows context menu"&gt;&lt;/p&gt;

&lt;p&gt;This will open another new panel, below which, in the lower panel's &lt;strong&gt;Data Output&lt;/strong&gt; tab, you can view all the data held within that table.&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/pgadmin/view_data_output.png" alt="View Data - example data output"&gt;&lt;/p&gt;

&lt;p&gt;With that, you've successfully created a table and populated it with some data through the pgAdmin web interface. Of course, this is just one method you can use to create a table through pgAdmin. For example, it's possible to create and populate a table using SQL instead of the GUI-based method described in this step. &lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;In this guide, you learned how to install pgAdmin 4 from a Python virtual environment, configure it, serve it to the web with Apache, and how to connect it to a PostgreSQL database. Additionally, this guide went over one method that can be used to create and populate a table, but pgAdmin can be used for much more than just creating and editing tables. &lt;/p&gt;

&lt;p&gt;For more information on how to get the most out of all of pgAdmin's features, we encourage you to review the &lt;a href="https://www.pgadmin.org/docs/pgadmin4/3.x/"&gt;project's documentation&lt;/a&gt;. You can also learn more about PostgreSQL through our &lt;a href="https://www.digitalocean.com/community/tags/postgresql?type=tutorials"&gt;Community tutorials&lt;/a&gt; on the subject.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-create-a-digitalocean-droplet-from-an-ubuntu-iso-format-image</id>
    <published>2018-10-17T21:46:57Z</published>
    <updated>2018-10-18T22:24:16Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-create-a-digitalocean-droplet-from-an-ubuntu-iso-format-image"/>
    <title>How to Create a DigitalOcean Droplet from an Ubuntu ISO Format Image</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;DigitalOcean's &lt;a href="https://www.digitalocean.com/docs/images/custom-images/"&gt;Custom Images&lt;/a&gt; feature allows you to bring your virtual disk images from an on-premise environment or another cloud platform to DigitalOcean and use them to start DigitalOcean Droplets.&lt;/p&gt;

&lt;p&gt;As described in the &lt;a href="https://www.digitalocean.com/docs/images/custom-images/overview/"&gt;Custom Images documentation&lt;/a&gt;, the following image types are supported natively by the Custom Images upload tool:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/IMG_(file_format)"&gt;Raw (&lt;code&gt;.img&lt;/code&gt;)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Qcow"&gt;qcow2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/VHD_(file_format)#Virtual_Hard_Disk_(VHDX)"&gt;VHDX&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/VirtualBox#VirtualBox_Disk_Image"&gt;VDI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/VMDK"&gt;VMDK&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/ISO_image"&gt;ISO&lt;/a&gt; is another popular image format which you may want to use with Custom Images. ISO images are frequently provided by Linux distributions as a convenient method for installing Linux. Unfortunately, ISO images aren't currently supported by the upload tool, although support is planned for the end of 2018.&lt;/p&gt;

&lt;p&gt;In this tutorial, we'll demonstrate how to use the free and open-source &lt;a href="https://www.virtualbox.org/"&gt;VirtualBox&lt;/a&gt; virtualization tool to create a DigitalOcean-compatible VDI image (VirtualBox Disk Image) from an Ubuntu 18.04 ISO. The steps in this guide can be adapted to work with your preferred distribution's ISO images.&lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;Before you begin, you'll need the following available to you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A local machine or remote server (with GUI access) onto which you'll install and use VirtualBox. In this tutorial we'll use a Mac OS X local machine, but you can use any system supported by VirtualBox. To learn more about supported systems, consult the &lt;a href="https://www.virtualbox.org/manual/ch01.html#hostossupport"&gt;VirtualBox Manual&lt;/a&gt;. The GUI menu options should be similar across operating systems, but may not be identical.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;An ISO-format &lt;a href="http://releases.ubuntu.com/18.04/"&gt;Ubuntu 18.04 Server OS image&lt;/a&gt;. The &lt;code&gt;ubuntu-18.04.1-live-server-amd64.iso&lt;/code&gt; image meets the two requirements listed in the Custom Images &lt;a href="https://www.digitalocean.com/docs/images/custom-images/overview/#image-requirements"&gt;Image Requirements&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your image must support the ext3 or ext4 filesystems&lt;/li&gt;
&lt;li&gt;Your image must have &lt;code&gt;cloud-init&lt;/code&gt; 0.7.7, &lt;code&gt;cloudbase-init&lt;/code&gt;, &lt;code&gt;coreos-cloudinit&lt;/code&gt;, &lt;code&gt;iginition&lt;/code&gt;, or &lt;code&gt;bsd-cloudinit&lt;/code&gt; installed (Ubuntu 18.04 Server comes with &lt;code&gt;cloud-init&lt;/code&gt; installed)&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're adapting these steps for another distribution's ISO and your image does not have &lt;code&gt;cloud-init&lt;/code&gt; installed and configured, you must install and configure it manually after installing the OS. &lt;/p&gt;

&lt;p&gt;Once you have these prerequisites available to you, you're ready to begin with this guide.&lt;/p&gt;

&lt;h2 id="step-1-—-installing-virtualbox-and-creating-a-virtual-machine"&gt;Step 1 — Installing VirtualBox and Creating a Virtual Machine&lt;/h2&gt;

&lt;p&gt;The tool we'll use to convert the ISO-format image in this guide is &lt;a href="https://www.virtualbox.org/"&gt;VirtualBox&lt;/a&gt;, a free and open-source virtualizer for x86 hardware. By default, VirtualBox uses a GUI, which we'll use to create the VDI image in this guide.&lt;/p&gt;

&lt;p&gt;To begin, download and install VirtualBox from the &lt;a href="https://www.virtualbox.org/wiki/Downloads"&gt;downloads&lt;/a&gt; page. Follow the appropriate link in the &lt;strong&gt;VirtualBox 5.2.20 platform packages&lt;/strong&gt; section depending on your host operating system. In this guide, we'll be using an OSX system, so we'll download and install VirtualBox using the provided DMG.&lt;/p&gt;

&lt;p&gt;Once you've installed VirtualBox, open the application.&lt;/p&gt;

&lt;p&gt;You should see the following welcome screen:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/iso_custom_images/vbox_welcome.png" alt="VirtualBox Welcome Screen"&gt;&lt;/p&gt;

&lt;p&gt;Click on &lt;strong&gt;New&lt;/strong&gt; to begin creating your Ubuntu virtual machine.&lt;/p&gt;

&lt;p&gt;The following window should pop up, allowing you to name your virtual machine (VM) and select its OS:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/iso_custom_images/name_os.png" alt="Name Virtual Machine Window"&gt;&lt;/p&gt;

&lt;p&gt;In this tutorial, we'll name our VM &lt;code&gt;Ubuntu 18.04&lt;/code&gt;, but feel free to give the VM a more descriptive name. &lt;/p&gt;

&lt;p&gt;For &lt;strong&gt;Type&lt;/strong&gt;, select &lt;strong&gt;Linux&lt;/strong&gt;, and for &lt;strong&gt;Version&lt;/strong&gt;, select &lt;strong&gt;Ubuntu (64-bit)&lt;/strong&gt;. Then, hit &lt;strong&gt;Continue&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The following screen should appear, allowing you to specify how much memory to allocate to your virtual machine:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/iso_custom_images/memory.png" alt="Allocate Memory Window"&gt;&lt;/p&gt;

&lt;p&gt;Unless you have a more complex use case, 1024 MB should be enough memory for your virtual machine. If you need to adjust memory size, enter the amount of memory to be allocated to the VM, then hit &lt;strong&gt;Continue&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You should see the following screen:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/iso_custom_images/hard_disk.png" alt="Create Hard Disk Window"&gt;&lt;/p&gt;

&lt;p&gt;This window allows you to create a virtual hard disk for your VM. This virtual hard disk is the image that you’ll upload to DigitalOcean in a later step. The Ubuntu operating system will be installed from the ISO you downloaded to this virtual hard disk. Make sure &lt;strong&gt;Create a virtual hard disk now&lt;/strong&gt; is selected, and hit &lt;strong&gt;Create&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The following &lt;strong&gt;Hard disk file type&lt;/strong&gt; window should appear, allowing you to select the format you'd like to use for your image:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/iso_custom_images/hard_disk_type.png" alt="Select Hard Disk Type Window"&gt;&lt;/p&gt;

&lt;p&gt;All three types are supported by DigitalOcean Custom Images, so unless you have a strong preference, select &lt;strong&gt;VDI (VirtualBox Disk Image)&lt;/strong&gt;. Hit &lt;strong&gt;Continue&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You should then see the following window:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/iso_custom_images/storage_option.png" alt="Hard Disk Options"&gt;&lt;/p&gt;

&lt;p&gt;This window allows you to choose between a &lt;strong&gt;Dynamically allocated&lt;/strong&gt; or &lt;strong&gt;Fixed size&lt;/strong&gt; hard disk file. We'll use the default &lt;strong&gt;Dynamically allocated&lt;/strong&gt; option and allow the file to grow as we install the Ubuntu OS and packages. Hit &lt;strong&gt;Continue&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The next window allows you to name your hard disk file (as well as choose the path to which it will be saved), and specify its maximum size:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/iso_custom_images/disk_file.png" alt="Hard Disk Size"&gt;&lt;/p&gt;

&lt;p&gt;Be sure to give yourself enough disk space to install the operating system as well as additional packages you may need. The default 10 GB should be fine for most purposes, but if you anticipate installing a large number of packages or storing a lot of data in the image, you should bump this up to your anticipated disk usage.&lt;/p&gt;

&lt;p&gt;Once you've selected the size of the virtual hard disk, hit &lt;strong&gt;Create&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;At this point, you'll be returned to the initial welcome screen, where you'll see the virtual machine you just created:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/iso_custom_images/vm_start.png" alt="VM Welcome Screen"&gt;&lt;/p&gt;

&lt;p&gt;We can now begin installing Ubuntu onto the virtual machine.&lt;/p&gt;

&lt;h2 id="step-2-—-installing-ubuntu-18-04-onto-the-virtual-machine"&gt;Step 2 — Installing Ubuntu 18.04 onto the Virtual Machine&lt;/h2&gt;

&lt;p&gt;In this step we'll install and configure the Ubuntu operating system onto our virtual machine.&lt;/p&gt;

&lt;p&gt;To begin, from the VirtualBox welcome screen, select your virtual machine, and hit the &lt;strong&gt;Start&lt;/strong&gt; button in the toolbar.&lt;/p&gt;

&lt;p&gt;You should see the following virtual machine window, prompting you to select the ISO file from which you'll boot the system:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/iso_custom_images/vm_install.png" alt="Select ISO"&gt;&lt;/p&gt;

&lt;p&gt;Select the Ubuntu 18.04 Server ISO you downloaded, and hit &lt;strong&gt;Start&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In the VM, the Ubuntu installer will begin booting from the ISO, and you should be brought to the following menu:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/iso_custom_images/ubuntu_language.png" alt="Ubuntu Select Language"&gt;&lt;/p&gt;

&lt;p&gt;Choose your preferred language using the arrow keys, and hit &lt;code&gt;ENTER&lt;/code&gt; to continue.&lt;/p&gt;

&lt;p&gt;You should then see the following &lt;strong&gt;Keyboard configuration&lt;/strong&gt; screen:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/iso_custom_images/keyboard.png" alt="Ubuntu Keyboard Config"&gt;&lt;/p&gt;

&lt;p&gt;Choose your preferred keyboard configuration, select &lt;strong&gt;Done&lt;/strong&gt;, and hit &lt;code&gt;ENTER&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Next, you'll be brought to the following installer selection screen:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/iso_custom_images/ubuntu_install.png" alt="Ubuntu Installer Selection"&gt;&lt;/p&gt;

&lt;p&gt;Select &lt;strong&gt;Install Ubuntu&lt;/strong&gt;, and hit &lt;code&gt;ENTER&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The following &lt;strong&gt;Network connections&lt;/strong&gt; screen should appear:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/iso_custom_images/network_connections.png" alt="Ubuntu Network connections"&gt;&lt;/p&gt;

&lt;p&gt;This screen allows you to configure the network interfaces for your Ubuntu server. Since we're performing the installation on a virtual machine, we'll just use the default option as the configured interface will be overwritten when we launch the image on the DigitalOcean platform.&lt;/p&gt;

&lt;p&gt;Select &lt;strong&gt;Done&lt;/strong&gt; and hit &lt;code&gt;ENTER&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You'll then be brought to the following &lt;strong&gt;Configure proxy&lt;/strong&gt; screen:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/iso_custom_images/configure_proxy.png" alt="Ubuntu Configure Proxy"&gt;&lt;/p&gt;

&lt;p&gt;If you require a proxy, enter it here. Then, select &lt;strong&gt;Done&lt;/strong&gt;, and hit &lt;code&gt;ENTER&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The next screen will allow you to choose an Ubuntu archive mirror:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/iso_custom_images/ubuntu_mirror.png" alt="Ubuntu Archive Mirror"&gt;&lt;/p&gt;

&lt;p&gt;Unless you require a specific mirror, the default should be fine here. Select &lt;strong&gt;Done&lt;/strong&gt; and hit &lt;code&gt;ENTER&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Next, you'll be prompted to partition your virtual disk:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/iso_custom_images/partition.png" alt="Ubuntu Partition Disk"&gt;&lt;/p&gt;

&lt;p&gt;Unless you'd like to set up &lt;a href="https://en.wikipedia.org/wiki/Logical_Volume_Manager_(Linux)"&gt;Logical Volume Manager&lt;/a&gt; (LVM) or manually partition the virtual disk, select &lt;strong&gt;Use An Entire Disk&lt;/strong&gt; to use the entire attached virtual disk, and hit &lt;code&gt;ENTER&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The following screen allows you to select the virtual disk that will be partitioned:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/iso_custom_images/filesystem_setup.png" alt="Ubuntu Filesystem setup"&gt;&lt;/p&gt;

&lt;p&gt;As described in the prompt text, the installer will create a partition for the bootloader, and use the remaining virtual disk space to create an &lt;code&gt;ext4&lt;/code&gt; partition to which the Ubuntu OS will be installed.&lt;/p&gt;

&lt;p&gt;Select the attached virtual disk and hit &lt;code&gt;ENTER&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The following screen displays a summary of the filesystem installer options before partitioning:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/iso_custom_images/filesystem_summary.png" alt="Ubuntu Filesystem Summary"&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;ext4&lt;/code&gt; partition will be mounted to &lt;code&gt;/&lt;/code&gt;, and a second partition (1 MB) will be created for the &lt;a href="https://en.wikipedia.org/wiki/GNU_GRUB"&gt;GRUB bootloader&lt;/a&gt;. Once you've gone over and confirmed the partitioning scheme for your virtual disk, select &lt;strong&gt;Done&lt;/strong&gt; and hit &lt;code&gt;ENTER&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;In the confirmation screen that appears, select &lt;strong&gt;Continue&lt;/strong&gt; and hit &lt;code&gt;ENTER&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The next screen will allow you to configure the system hostname, as well as an Ubuntu user:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/iso_custom_images/profile_setup.png" alt="Ubuntu Create User"&gt;&lt;/p&gt;

&lt;p&gt;Note that as you fill out this screen, the installer will continue copying files to the virtual disk in the background.&lt;/p&gt;

&lt;p&gt;In this tutorial, we'll create a user named &lt;strong&gt;sammy&lt;/strong&gt; and call our server &lt;strong&gt;ubuntu&lt;/strong&gt;. The server name will likely be overwritten when this image is run on the DigitalOcean platform, so feel free to give it a temporary name here. &lt;/p&gt;

&lt;p&gt;You can upload your SSH keys to DigitalOcean and automatically embed them into created Droplets, so for now we won't &lt;strong&gt;Import SSH identity&lt;/strong&gt;. To learn how to upload your SSH keys to DigitalOcean, consult the &lt;a href="https://www.digitalocean.com/docs/droplets/how-to/add-ssh-keys/to-account/"&gt;Droplet Product Documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Once you've filled in all the required fields, the prompt should look something like this:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/iso_custom_images/profile_setup_complete.png" alt="Ubuntu Profile Complete"&gt;&lt;/p&gt;

&lt;p&gt;Select &lt;strong&gt;Done&lt;/strong&gt; and hit &lt;code&gt;ENTER&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The next screen will prompt you to select popular snaps for your Ubuntu server. Snaps are prepackaged bundles of software that contain an application, its dependencies, and configuration. To learn more about snaps, consult the &lt;a href="https://docs.snapcraft.io/snap-documentation/"&gt;Snap Documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/iso_custom_images/ubuntu_snaps.png" alt="Ubuntu Select Snaps"&gt;&lt;/p&gt;

&lt;p&gt;In this guide we won't install any snaps and will manually install packages in a later step. If you'd like to install a snap, select or deselect it using &lt;code&gt;SPACE&lt;/code&gt; and scroll down to &lt;strong&gt;Done&lt;/strong&gt;. Then, hit &lt;code&gt;ENTER&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Regardless of your selection in the snap screen, you'll then be brought to an installation progress and summary screen:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/iso_custom_images/install_progress.png" alt="Ubuntu Install Progress"&gt;&lt;/p&gt;

&lt;p&gt;Once the installation completes, select &lt;strong&gt;Reboot Now&lt;/strong&gt; and hit &lt;code&gt;ENTER&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The installer will shut down and prompt you to remove the installation medium (in this case this is the ISO image we selected earlier). In most cases, the ISO will be detached automatically upon reboot, so you can simply hit &lt;code&gt;ENTER&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;To double check, in the VirtualBox GUI menu, navigate to &lt;strong&gt;Devices&lt;/strong&gt;, and then &lt;strong&gt;Optical Drives&lt;/strong&gt;. If the &lt;strong&gt;Remove disk from virtual drive&lt;/strong&gt; option is available to you, click on it to detach the ISO from the virtual machine. Then, back in the virtual machine window, hit &lt;code&gt;ENTER&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The system will reboot in the virtual machine, this time from the virtual disk to which we installed Ubuntu.&lt;/p&gt;

&lt;p&gt;Since &lt;code&gt;cloud-init&lt;/code&gt; is installed by default on Ubuntu 18.04 Server, the first time Ubuntu boots, &lt;code&gt;cloud-init&lt;/code&gt; will run and configure itself. In the virtual machine window, you should see some &lt;code&gt;cloud-init&lt;/code&gt; log items and have a prompt available to you. Hit &lt;code&gt;ENTER&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;You can then log in to your Ubuntu server using the user you created in the installer.&lt;/p&gt;

&lt;p&gt;Enter your username and hit &lt;code&gt;ENTER&lt;/code&gt;, then enter your password and hit &lt;code&gt;ENTER&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You should now have access to a command prompt, indicating that you've successfully completed the Ubuntu 18.04 installation, and are now logged in as the user you created previously.&lt;/p&gt;

&lt;p&gt;In the next step of this guide, we'll reconfigure &lt;code&gt;cloud-init&lt;/code&gt; and set it up to run when the Ubuntu image is launched as a Droplet on the DigitalOcean platform.&lt;/p&gt;

&lt;h2 id="step-3-—-reconfiguring-cloud-init"&gt;Step 3 — Reconfiguring &lt;code&gt;cloud-init&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;Now that we've installed Ubuntu 18.04 to a virtual disk and have the system up and running, we need to reconfigure &lt;code&gt;cloud-init&lt;/code&gt; to use the appropriate datasource for the DigitalOcean platform. A &lt;code&gt;cloud-init&lt;/code&gt; datasource is a source of config data for &lt;code&gt;cloud-init&lt;/code&gt; that typically consists of userdata (like shell scripts) or server metadata, like hostname, instance-id, etc. To learn more about &lt;code&gt;cloud-init&lt;/code&gt; datasources, consult the &lt;a href="https://cloudinit.readthedocs.io/en/latest/topics/datasources.html"&gt;official &lt;code&gt;cloud-init&lt;/code&gt; docs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;By default, on Ubuntu 18.04, &lt;code&gt;cloud-init&lt;/code&gt; configures itself to use the &lt;code&gt;DataSourceNoCloud&lt;/code&gt; datasource. This will cause problems when running the image on DigitalOcean, so we need to reconfigure &lt;code&gt;cloud-init&lt;/code&gt; to use the &lt;code&gt;ConfigDrive&lt;/code&gt; datasource and ensure that &lt;code&gt;cloud-init&lt;/code&gt; reruns when the image is launched on DigitalOcean.&lt;/p&gt;

&lt;p&gt;To begin, ensure that you've started your Ubuntu 18.04 virtual machine and have logged in as the user you created earlier.&lt;/p&gt;

&lt;p&gt;From the command line, navigate to the &lt;code&gt;/etc/cloud/cloud.cfg.d&lt;/code&gt; directory:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cd /etc/cloud/cloud.cfg.d
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Use the &lt;code&gt;ls&lt;/code&gt; command to list the &lt;code&gt;cloud-init&lt;/code&gt; config files present in the directory:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;ls
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;05_logging.cfg  50-curtin-networking.cfg  90_dpkg.cfg  curtin-preserve-sources.cfg  README
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;First, delete the &lt;code&gt;50-curtin-networking.cfg&lt;/code&gt; file, which configures networking interfaces for your Ubuntu server. When the image is launched on DigitalOcean, &lt;code&gt;cloud-init&lt;/code&gt; will run and reconfigure these interfaces automatically. If this file is not deleted, the DigitalOcean Droplet created from this Ubuntu image will have its interfaces misconfigured and won't be accessible from the internet.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo rm 50-curtin-networking.cfg
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, we'll run &lt;code&gt;dpkg-reconfigure cloud-init&lt;/code&gt; to remove the &lt;code&gt;NoCloud&lt;/code&gt; datasource, ensuring that &lt;code&gt;cloud-init&lt;/code&gt; searches for and finds the &lt;code&gt;ConfigDrive&lt;/code&gt; datasource used on DigitalOcean:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo dpkg-reconfigure cloud-init
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see the following graphical menu:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/iso_custom_images/cloud_init.png" alt="Cloud Init dpkg Menu"&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;NoCloud&lt;/code&gt; datasource is initially highlighted. Press &lt;code&gt;SPACE&lt;/code&gt; to unselect it, then hit &lt;code&gt;ENTER&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Finally, navigate to &lt;code&gt;/etc/netplan&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cd /etc/netplan
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Remove the &lt;code&gt;50-cloud-init.yaml&lt;/code&gt; file (this was generated from the &lt;code&gt;cloud-init&lt;/code&gt; networking file we removed earlier):&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo rm 50-cloud-init.yaml
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The final step is ensuring that we clean up configuration from the initial &lt;code&gt;cloud-init&lt;/code&gt; run so that it reruns when the image is launched on DigitalOcean.&lt;/p&gt;

&lt;p&gt;To do this, run &lt;code&gt;cloud-init clean&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo cloud-init clean
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At this point, your image is ready to be launched on the DigitalOcean platform. You can install additional packages and software into your image. Once you're done, shutdown your virtual machine:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo shutdown -h now
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can now move on to uploading and launching this custom image on the DigitalOcean platform.&lt;/p&gt;

&lt;h2 id="step-4-—-uploading-custom-image-and-creating-droplet"&gt;Step 4 — Uploading Custom Image and Creating Droplet&lt;/h2&gt;

&lt;p&gt;Now that we've created an Ubuntu 18.04 VDI image and configured it for use on DigitalOcean, we can upload it using the Custom Images &lt;a href="https://www.digitalocean.com/docs/images/custom-images/quickstart/"&gt;upload tool&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;On macOS, the Ubuntu virtual disk image we created and configured will be located by default at &lt;code&gt;~/VirtualBox VMs/&lt;span class="highlight"&gt;your_VM_name&lt;/span&gt;/&lt;span class="highlight"&gt;your_virtual_disk_name&lt;/span&gt;.vdi&lt;/code&gt;. This path may vary slightly depending on the OS you're using with VirtualBox. &lt;/p&gt;

&lt;p&gt;Before we upload the image, we'll compress it to speed up the file transfer to DigitalOcean.&lt;/p&gt;

&lt;p&gt;On your host OS (not inside the virtual machine), navigate to the directory containing your VDI image file:&lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cd ~/VirtualBox\ VMs/&lt;span class="highlight"&gt;Ubuntu\ 18.04&lt;/span&gt;/
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, use &lt;code&gt;gzip&lt;/code&gt; to compress the file:&lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;gzip &amp;lt; &lt;span class="highlight"&gt;Ubuntu\ 18.04.vdi&lt;/span&gt; &amp;gt; &lt;span class="highlight"&gt;Ubuntu\ 18.04&lt;/span&gt;.gz
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this command we pipe the source &lt;code&gt;Ubuntu 18.04.vdi&lt;/code&gt; file into &lt;code&gt;gzip&lt;/code&gt;, specifying as output the &lt;code&gt;Ubuntu 18.04.gz&lt;/code&gt; compressed file. &lt;/p&gt;

&lt;p&gt;Once &lt;code&gt;gzip&lt;/code&gt; finishes compressing your file, upload the &lt;code&gt;.gz&lt;/code&gt; file to DigitalOcean, following instructions in the &lt;a href="https://www.digitalocean.com/docs/images/custom-images/quickstart/"&gt;Custom Images Quickstart&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You should now be able to create and use Droplets from your custom Ubuntu 18.04 Server image.&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;In this tutorial, we learned how to create a custom VDI image from a vanilla Ubuntu 18.04 ISO using the VirtualBox virtualization tool. We adjusted &lt;code&gt;cloud-init&lt;/code&gt; so it can properly configure Droplet networking on DigitalOcean, and finally compressed and uploaded the image using the Custom Images upload tool.&lt;/p&gt;

&lt;p&gt;You can adjust the steps in this tutorial to work with your preferred Linux distribution’s ISO images. Ensure that you have an SSH server installed and configured to start on boot, and that &lt;code&gt;cloud-init&lt;/code&gt; has been installed and properly configured to use the &lt;code&gt;ConfigDrive&lt;/code&gt; datasource. Finally, ensure that any stale networking configuration files have been purged. &lt;/p&gt;

&lt;p&gt;You may also wish to use a tool like &lt;a href="https://www.packer.io/"&gt;Packer&lt;/a&gt; to automate the creation of your machine images.&lt;/p&gt;

&lt;p&gt;To learn more about DigitalOcean Custom Images, consult the Custom Images &lt;a href="https://www.digitalocean.com/docs/images/custom-images/"&gt;product docs&lt;/a&gt; and launch &lt;a href="https://blog.digitalocean.com/custom-images/"&gt;blog post&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-deploy-a-symfony-4-application-to-production-with-lemp-on-ubuntu-18-04</id>
    <published>2018-10-16T16:46:49Z</published>
    <updated>2018-10-18T16:29:11Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-deploy-a-symfony-4-application-to-production-with-lemp-on-ubuntu-18-04"/>
    <title>How to Deploy a Symfony 4 Application to Production with LEMP on Ubuntu 18.04</title>
    <content type="html">&lt;p&gt;&lt;em&gt;The author selected &lt;a href="https://www.brightfunds.org/organizations/software-in-the-public-interest-inc"&gt;Software in the Public Interest Inc&lt;/a&gt; to receive a donation as part of the &lt;a href="https://do.co/w4do-cta"&gt;Write for DOnations&lt;/a&gt; program.&lt;/em&gt;&lt;/p&gt;

&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://symfony.com"&gt;Symfony&lt;/a&gt; is an open-source PHP framework with an elegant structure and a reputation for being a suitable framework to kick-start any project irrespective of its size. As a set of reusable components, its flexibility, architecture, and high performance make it a top choice for building a highly complex enterprise application.&lt;/p&gt;

&lt;p&gt;In this tutorial, you will deploy an existing, standard Symfony 4 application to production with a LEMP stack (Nginx, MySQL, and PHP) on Ubuntu 18.04, which will help you get started configuring the server and the structure of the framework. Nginx is a popular open-source, high-performance HTTP server with additional features including reverse proxy support. It has a good reputation and hosts some of the largest and highest traffic sites on the internet. If you choose to deploy your own Symfony application instead, you might have to implement extra steps depending on the existing structure of your application.&lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;To complete this tutorial, you will need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One Ubuntu 18.04 server set up by following &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-18-04"&gt;the Ubuntu 18.04 initial server setup guide&lt;/a&gt;, including a non-root user with &lt;code&gt;sudo&lt;/code&gt; access and a firewall.&lt;/li&gt;
&lt;li&gt;Nginx, MySQL, and PHP installed by following &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-linux-nginx-mysql-php-lemp-stack-ubuntu-18-04"&gt;How To Install Linux, Nginx, MySQL, PHP (LEMP stack) on Ubuntu 18.04 &lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Composer installed following steps 1 and 2 of &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-and-use-composer-on-ubuntu-18-04"&gt;How to Install and Use Composer on Ubuntu 18.04&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Git installed by following &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-git-on-ubuntu-18-04"&gt;How to Install Git on Ubuntu 18.04&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="step-1-—-creating-a-user-and-database-for-the-application"&gt;Step 1 — Creating a User and Database for the Application&lt;/h2&gt;

&lt;p&gt;By following the instructions in the Prerequisites, you now have all the basic server dependencies required for the application installation. As every dynamic web application requires a database, you will create a user and properly configure a database for the application in this section.&lt;/p&gt;

&lt;p&gt;To create a MySQL database for our application and a user associated with it, you need to access the MySQL client using the MySQL root account:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt; mysql -u root -p
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Enter the appropriate password, which should be the same password used when running &lt;code&gt;mysql_secure_installation&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Next, create the application database with:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;CREATE DATABASE blog;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will see the following output in the console:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Query OK, 1 row affected (0.00 sec)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You have successfully created your application database. You can now create a MySQL user and grant them access to the newly created database.&lt;/p&gt;

&lt;p&gt;Execute the following command to create a MySQL user and password. You can change the username and password to something more secure if you wish:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;CREATE USER  &lt;span class="highlight"&gt;'blog-admin'&lt;/span&gt;@'localhost' IDENTIFIED BY &lt;span class="highlight"&gt;'password'&lt;/span&gt;;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will see the following output:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Query OK, 0 rows affected (0.00 sec)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Currently, the user &lt;strong&gt;blog-admin&lt;/strong&gt; does not have the right permission over the application database. In fact, even if &lt;strong&gt;blog-admin&lt;/strong&gt; tries to log-in with their password, they will not be able to reach the MySQL shell.&lt;/p&gt;

&lt;p&gt;A user needs the right permission before accessing or carrying out a specific action on a database. Use the following command to allow complete access to the &lt;strong&gt;blog&lt;/strong&gt; database for the &lt;strong&gt;blog-admin&lt;/strong&gt; user:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;GRANT ALL PRIVILEGES ON blog.* TO &lt;span class="highlight"&gt;'blog-admin'&lt;/span&gt;@'localhost';
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will see the following output:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Query OK, 0 rows affected (0.00 sec)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;strong&gt;blog-admin&lt;/strong&gt; now has all privileges on all the tables inside the &lt;strong&gt;blog&lt;/strong&gt; database. To reload the grant tables and apply changes, you need to perform a flush-privilege operation using the flush statement:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;FLUSH PRIVILEGES;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will see the following output:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Query OK, 0 rows affected (0.00 sec)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You are done creating a new user and granting privileges. To test if you’re on track, exit the MySQL client:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;quit;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And log in again, using the credentials of the MySQL user you just created and enter the password when prompted:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;mysql -u &lt;span class="highlight"&gt;blog-admin&lt;/span&gt; -p
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Check that the database can be accessed by the user with:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;SHOW DATABASES;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You'll see the &lt;code&gt;blog&lt;/code&gt; table in the output:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;+--------------------+
| Database           |
+--------------------+
| information_schema |
| blog               |
+--------------------+
2 rows in set (0.00 sec)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, exit the MySQL client:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;quit;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You have successfully created a database, a user for the demo application, and granted the newly created user the right privileges to access the database. You are now ready to set up the demo application.&lt;/p&gt;

&lt;h2 id="step-2-—-setting-up-the-demo-application"&gt;Step 2 — Setting Up the Demo Application&lt;/h2&gt;

&lt;p&gt;To keep this tutorial simple, you will deploy a blog application built with Symfony. This application will allow an authenticated user to create a blog post and store it in the database. In addition, the application user can view all the posts and details associated with an author.&lt;/p&gt;

&lt;p&gt;The source code of the blog application you will deploy in this tutorial is &lt;a href="https://github.com/yemiwebby/symfony-blog"&gt;on GitHub&lt;/a&gt;. You will use &lt;a href="https://git-scm.com/"&gt;Git&lt;/a&gt; to pull the source code of the application from GitHub and save it in a new directory.&lt;/p&gt;

&lt;p&gt;First, create a directory that will serve as the root directory for your application. So, run the following command from the console to create a new directory named &lt;code&gt;symfony-blog&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo mkdir -p /var/www/symfony-blog
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In order to work with the project files using a non-root user account, you’ll need to change the folder owner and group by running:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt; sudo chown &lt;span class="highlight"&gt;sammy&lt;/span&gt;:&lt;span class="highlight"&gt;sammy&lt;/span&gt; /var/www/symfony-blog
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Replace &lt;strong&gt;sammy&lt;/strong&gt; with your sudo non-root username.&lt;/p&gt;

&lt;p&gt;Now, you can change into the parent directory and clone the application on GitHub:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cd /var/www
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;git clone https://github.com/yemiwebby/symfony-blog.git symfony-blog
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You'll see the following output:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Cloning into 'symfony-blog'...
remote: Counting objects: 180, done.
remote: Compressing objects: 100% (122/122), done.
remote: Total 180 (delta 57), reused 164 (delta 41), pack-reused 0
Receiving objects: 100% (180/180), 167.01 KiB | 11.13 MiB/s, done.
Resolving deltas: 100% (57/57), done.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The demo application is now set. In the next step, you will configure the environment variables and install the required dependencies for the project.&lt;/p&gt;

&lt;h2 id="step-3-—-configuring-your-environment-variables-for-the-application"&gt;Step 3 — Configuring your Environment Variables for the Application&lt;/h2&gt;

&lt;p&gt;To completely set up the application, you need to install the project dependencies and properly configure the application parameters.&lt;/p&gt;

&lt;p&gt;By default, the Symfony application runs in a development mode, which gives it a very detailed log for the purposes of debugging. This is not applicable to what you are doing in this tutorial, and not good practice for a production environment, as it can slow things down and create very large log files.&lt;/p&gt;

&lt;p&gt;Symfony needs to be aware that you’re running the application in a production environment. You can set this up by either creating a &lt;code&gt;.env&lt;/code&gt; file containing variable declarations, or creating environment variables directly. Since you can also use the &lt;code&gt;.env&lt;/code&gt; file to configure your database credentials for this application, it makes more sense for you to do this. Change your working directory to the cloned project and create the &lt;code&gt;.env&lt;/code&gt; file with:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cd symfony-blog
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo nano .env
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add the following lines to the file to configure the production application environment:&lt;/p&gt;
&lt;div class="code-label " title=".env"&gt;.env&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-nginx"&gt;APP_ENV=prod
APP_DEBUG=0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;APP_ENV&lt;/code&gt; is an environment variable that specifies that the application is in production, while &lt;code&gt;APP_DEBUG&lt;/code&gt; is an environment variable that specifies if the application should run in debug mode or not. You have set it to false for now.&lt;/p&gt;

&lt;p&gt;Save the file and exit the editor.&lt;/p&gt;

&lt;p&gt;Next, install a PHP extension that Symfony apps use to handle XML:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install php7.2-xml
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, you need to install the project dependencies, run &lt;code&gt;composer install&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cd /var/www/symfony-blog
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;composer install
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You have successfully configured the environment variables and installed the required dependencies for the project. Next, you will set up the database credentials.&lt;/p&gt;

&lt;h2 id="step-4-—-setting-up-database-credentials"&gt;Step 4 — Setting Up Database Credentials&lt;/h2&gt;

&lt;p&gt;In order to retrieve data from the application’s database you created earlier, you will need to set up and configure the required database credentials from within the Symfony application.&lt;/p&gt;

&lt;p&gt;Open the &lt;code&gt;.env&lt;/code&gt; file again:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano .env
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add the following content to the file, which will allow you to easily connect and interact properly with the database. You can add it right after the &lt;code&gt;APP_DEBUG=0&lt;/code&gt; line within the &lt;code&gt;.env&lt;/code&gt; file:&lt;/p&gt;
&lt;div class="code-label " title=".env"&gt;.env&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-nginx"&gt;...
DATABASE_URL=mysql://&lt;span class="highlight"&gt;blog-admin&lt;/span&gt;:&lt;span class="highlight"&gt;password&lt;/span&gt;@localhost:3306/blog
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The Symfony framework uses a third-party library called &lt;a href="http://www.doctrine-project.org/"&gt;Doctrine&lt;/a&gt; to communicate with databases. Doctrine gives you useful tools to make interactions with databases easy and flexible.&lt;/p&gt;

&lt;p&gt;You can now use Doctrine to update your database with the tables from the cloned Github application. Run this command to do that:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;php bin/console doctrine:schema:update --force
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You'll see the following output:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Updating database schema...
    4 queries were executed
[OK] Database schema updated successfully!  
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After setting up the required credentials and updating the database schema, you can now easily interact with the database. In order to start the application with some data, you will load a set of dummy data into the database in the next section.&lt;/p&gt;

&lt;h2 id="step-5-—-populating-your-database-using-doctrine-fixtures"&gt;Step 5 — Populating your Database Using Doctrine-Fixtures&lt;/h2&gt;

&lt;p&gt;At the moment, the newly created tables are empty. You will populate it using &lt;a href="https://symfony.com/doc/master/bundles/DoctrineFixturesBundle/index.html"&gt;doctrine-fixtures&lt;/a&gt;. Using Doctrine-Fixtures is not a prerequisite for Symfony applications, it is only used to provide dummy data for your application.&lt;/p&gt;

&lt;p&gt;Run the following command to automatically load testing data that contains the details of an &lt;strong&gt;author&lt;/strong&gt; and a sample &lt;strong&gt;post&lt;/strong&gt; into the database table created for the blog:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;php bin/console doctrine:fixtures:load
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will get a warning about the database getting purged. You can go ahead and type &lt;code&gt;Y&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Careful, database will be purged. Do you want to continue y/N ? y
  &amp;gt; purging database
  &amp;gt; loading App\DataFixtures\ORM\Fixtures  
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the next section you will clear and warm up you cache.&lt;/p&gt;

&lt;h2 id="step-6-—-clearing-and-warming-up-your-cache"&gt;Step 6 — Clearing and Warming Up your Cache&lt;/h2&gt;

&lt;p&gt;To ensure your application loads faster when users make requests, it is good practice to warm the cache during the deployment. Warming up the cache generates pages and stores them for faster responses later rather than building completely new pages. Fortunately, Symfony has a command to clear the cache that also triggers a warm up. Run the following command for that purpose:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;php bin/console cache:clear
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will see the following output:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Clearing the cache for the prod environment with debug false
[OK] Cache for the "prod" environment (debug=false) was successfully cleared.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will conclude the set up in a bit. All that remains is to configure the web server. You will do that in the next section.&lt;/p&gt;

&lt;h2 id="step-7-—-configuring-the-web-server-and-running-the-application"&gt;Step 7 — Configuring the Web Server and Running the Application&lt;/h2&gt;

&lt;p&gt;By now, you have Nginx installed to serve your pages and MySQL to store and manage your data. You will now configure the web server by creating a new application server block, instead of editing the default one.&lt;/p&gt;

&lt;p&gt;Open a new server block with:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/nginx/sites-available/blog
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add the following content to the new server block configuration file. Ensure you replace the &lt;code&gt;&lt;span class="highlight"&gt;your_server_ip&lt;/span&gt;&lt;/code&gt; within the server block with your server IP address:&lt;/p&gt;
&lt;div class="code-label " title="/etc/nginx/sites-available/blog"&gt;/etc/nginx/sites-available/blog&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-nginx"&gt;
server {
    listen 80;
    listen [::]:80;

    server_name blog &lt;span class="highlight"&gt;your_server_ip&lt;/span&gt;;
    root /var/www/symfony-blog/public;
    index index.php;
    client_max_body_size 100m;

    location / {
        try_files $uri $uri/ /index.php$is_args$args;
    }

    location ~ \.php {
        try_files $uri /index.php =404;
        fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param SCRIPT_NAME $fastcgi_script_name;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_index index.php;
        include fastcgi_params;
      }

    location ~ /\.(?:ht|git|svn) {
        deny all;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;First, we specified the &lt;code&gt;listen&lt;/code&gt; directives for Nginx, which is by default on port &lt;code&gt;80&lt;/code&gt;, and then set the server name to match requests for the server’s IP address. Next, we used the &lt;code&gt;root&lt;/code&gt;  directives to specify the document root for the project. The &lt;code&gt;symfony-blog&lt;/code&gt; application is stored in &lt;code&gt;/var/www/symfony-blog&lt;/code&gt;, but to comply with best practices, we set the web root to &lt;code&gt;/var/www/symfony-blog/public&lt;/code&gt; as only the &lt;code&gt;/public&lt;/code&gt; subdirectory should be exposed to the internet. Finally, we configured the location directive to handle PHP processing.&lt;/p&gt;

&lt;p&gt;After adding the content, save the file and exit the editor.&lt;/p&gt;

&lt;p&gt;&lt;span class='note'&gt;&lt;strong&gt;Note:&lt;/strong&gt; If you created the file &lt;code&gt;&lt;span class="highlight"&gt;example.com&lt;/span&gt;&lt;/code&gt; in the prerequisite article How To Install Linux, Nginx, MySQL, PHP (LEMP stack) on Ubuntu 18.04, remove it from the &lt;code&gt;sites-enabled&lt;/code&gt; directory with &lt;code&gt;sudo rm /etc/nginx/sites-enabled/&lt;span class="highlight"&gt;example.com&lt;/span&gt;&lt;/code&gt; so it doesn't conflict with this new file.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;To enable the newly created server block, we need to create a symbolic link from the new server block configuration file located in &lt;code&gt;/etc/nginx/sites-available&lt;/code&gt; directory to the &lt;code&gt;/etc/nginx/sites-enabled&lt;/code&gt; by using the following command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ln -s /etc/nginx/sites-available/blog /etc/nginx/sites-enabled/
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Check the new configuration file for any syntax errors by running:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nginx -t
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This command will print errors to the console if there are any. Once there are no errors run this command to reload Nginx:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt; sudo systemctl reload nginx
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You just concluded the last step required to successfully deploy the Symfony 4 application. You configured the web server by creating a server block and properly set the web root in order to make the web application accessible.&lt;/p&gt;

&lt;p&gt;Finally, you can now run and test out the application. Visit &lt;code&gt;http://&lt;span class="highlight"&gt;your_server_ip&lt;/span&gt;&lt;/code&gt; in your favorite browser:&lt;/p&gt;

&lt;p&gt;The following image is the screenshot of the Symfony blog application that you should see at your server's IP address:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/symfony4-1804/EQQNuIv.png" alt="Alt screenshot of the Symfony blog application"&gt;&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Symfony is a feature-rich PHP framework with an architecture that makes web development fun for the developer who builds software using it. Symfony is a feature-rich web development framework that provides developers powerful tools to build web applications. It's often considered a good choice for enterprise applications due to its flexibility. The steps to deploy a typical Symfony application vary—depending on the setup, complexity, and the requirements of the application.&lt;/p&gt;

&lt;p&gt;In this tutorial, you manually deployed a Symfony 4 application to production on an Ubuntu 18.04 server running LEMP. You can now apply this knowledge to deploying your own Symfony applications.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/introduction-to-queries-mysql</id>
    <published>2018-10-17T19:05:28Z</published>
    <updated>2018-10-17T20:53:10Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/introduction-to-queries-mysql"/>
    <title>An Introduction to Queries in MySQL</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;Databases are a key component of many websites and applications, and are at the core of how data is stored and exchanged across the internet. One of the most important aspects of database management is the practice of retrieving data from a database, whether it's on an ad hoc basis or part of a process that's been coded into an application. There are several ways to retrieve information from a database, but one of the most commonly-used methods is performed through submitting &lt;em&gt;queries&lt;/em&gt; through the command line. &lt;/p&gt;

&lt;p&gt;In relational database management systems, a &lt;em&gt;query&lt;/em&gt; is any command used to retrieve data from a table. In Structured Query Language (SQL), queries are almost always made using the &lt;code&gt;SELECT&lt;/code&gt; statement. &lt;/p&gt;

&lt;p&gt;In this guide, we will discuss the basic syntax of SQL queries as well as some of the more commonly-employed functions and operators. We will also practice making SQL queries using some sample data in a MySQL database.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.mysql.com/"&gt;MySQL&lt;/a&gt; is an open-source relational database management system. One of the most widely-deployed SQL-databases, MySQL prioritizes speed, reliability, and usability. It generally follows the ANSI SQL standard, although there are a few cases where MySQL performs operations differently than the recognized standard.&lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;In general, the commands and concepts presented in this guide can be used on any Linux-based operating system running any SQL database software. However, it was written specifically with an Ubuntu 18.04 server running MySQL in mind. To set this up, you will need the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An Ubuntu 18.04 machine with a non-root user with sudo privileges. This can be set up using our &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-18-04"&gt;Initial Server Setup guide for Ubuntu 18.04&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;MySQL installed on the machine. Our guide on &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-mysql-on-ubuntu-18-04"&gt;How to Install MySQL on Ubuntu 18.04&lt;/a&gt; can help you set this up.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With this setup in place, we can begin the tutorial.&lt;/p&gt;

&lt;h2 id="creating-a-sample-database"&gt;Creating a Sample Database&lt;/h2&gt;

&lt;p&gt;Before we can begin making queries in SQL, we will first create a database and a couple tables, then populate these tables with some sample data. This will allow you to gain some hands-on experience when you begin making queries later on.&lt;/p&gt;

&lt;p&gt;For the sample database we'll use throughout this guide, imagine the following scenario:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;You and several of your friends all celebrate your birthdays with one another. On each occasion, the members of the group head to the local bowling alley, participate in a friendly tournament, and then everyone heads to your place where you prepare the birthday-person's favorite meal.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Now that this tradition has been going on for a while, you've decided to begin tracking the records from these tournaments. Also, to make planning dinners easier, you decide to create a record of your friends' birthdays and their favorite entrees, sides, and desserts. Rather than keep this information in a physical ledger, you decide to exercise your database skills by recording it in a MySQL database.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;To begin, open up a MySQL prompt as your &lt;strong&gt;root&lt;/strong&gt; MySQL user:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo mysql
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;span class='note'&gt;&lt;p&gt;
&lt;strong&gt;Note:&lt;/strong&gt; If you followed the prerequisite the tutorial on &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-mysql-on-ubuntu-18-04"&gt;Installing MySQL on Ubuntu 18.04&lt;/a&gt;, you may have configured your &lt;strong&gt;root&lt;/strong&gt; user to authenticate using a password. In this case, you will connect to the MySQL prompt with the following command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;mysql -u root -p
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;/p&gt;&lt;/span&gt;

&lt;p&gt;Next, create the database by running:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;CREATE DATABASE `birthdays`;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then select this database by typing:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;USE birthdays;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, create two tables within this database. We'll use the first table to track your friends' records at the bowling alley. The following command will create a table called &lt;code&gt;tourneys&lt;/code&gt; with columns for the &lt;code&gt;name&lt;/code&gt; of each of your friends, the number of tournaments they've won (&lt;code&gt;wins&lt;/code&gt;), their all-time &lt;code&gt;best&lt;/code&gt; score, and what size bowling shoe they wear (&lt;code&gt;size&lt;/code&gt;):&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;CREATE TABLE tourneys ( 
&lt;/li&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;name varchar(30), 
&lt;/li&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;wins real, 
&lt;/li&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;best real, 
&lt;/li&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;size real 
&lt;/li&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;);
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once you run the &lt;code&gt;CREATE TABLE&lt;/code&gt; command and populate it with column headings, you’ll receive the following output:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Query OK, 0 rows affected (0.00 sec)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Populate the &lt;code&gt;tourneys&lt;/code&gt; table with some sample data:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;INSERT INTO tourneys (name, wins, best, size) 
&lt;/li&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;VALUES ('Dolly', '7', '245', '8.5'), 
&lt;/li&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;('Etta', '4', '283', '9'), 
&lt;/li&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;('Irma', '9', '266', '7'), 
&lt;/li&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;('Barbara', '2', '197', '7.5'), 
&lt;/li&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;('Gladys', '13', '273', '8');
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You’ll receive an output like this:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Query OK, 5 rows affected (0.01 sec)
Records: 5  Duplicates: 0  Warnings: 0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Following this, create another table within the same database which we'll use to store information about your friends' favorite birthday meals. The following command creates a table named &lt;code&gt;dinners&lt;/code&gt; with columns for the &lt;code&gt;name&lt;/code&gt; of each of your friends, their &lt;code&gt;birthdate&lt;/code&gt;, their favorite &lt;code&gt;entree&lt;/code&gt;, their preferred &lt;code&gt;side&lt;/code&gt; dish, and their favorite &lt;code&gt;dessert&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;CREATE TABLE dinners ( 
&lt;/li&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;name varchar(30), 
&lt;/li&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;birthdate date, 
&lt;/li&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;entree varchar(30), 
&lt;/li&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;side varchar(30), 
&lt;/li&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;dessert varchar(30) 
&lt;/li&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;);
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Similarly for this table, you’ll receive feedback confirming that the command ran successfully:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Query OK, 0 rows affected (0.01 sec)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Populate this table with some sample data as well:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;INSERT INTO dinners (name, birthdate, entree, side, dessert) 
&lt;/li&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;VALUES ('Dolly', '1946-01-19', 'steak', 'salad', 'cake'), 
&lt;/li&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;('Etta', '1938-01-25', 'chicken', 'fries', 'ice cream'), 
&lt;/li&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;('Irma', '1941-02-18', 'tofu', 'fries', 'cake'), 
&lt;/li&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;('Barbara', '1948-12-25', 'tofu', 'salad', 'ice cream'), 
&lt;/li&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;('Gladys', '1944-05-28', 'steak', 'fries', 'ice cream');
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Query OK, 5 rows affected (0.00 sec)
Records: 5  Duplicates: 0  Warnings: 0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once that command completes successfully, you're done setting up your database. Next, we'll go over the basic command structure of &lt;code&gt;SELECT&lt;/code&gt; queries.&lt;/p&gt;

&lt;h2 id="understanding-select-statements"&gt;Understanding SELECT Statements&lt;/h2&gt;

&lt;p&gt;As mentioned in the introduction, SQL queries almost always begin with the &lt;code&gt;SELECT&lt;/code&gt; statement. &lt;code&gt;SELECT&lt;/code&gt; is used in queries to specify which columns from a table should be returned in the result-set. Queries also almost always include &lt;code&gt;FROM&lt;/code&gt;, which is used to specify which table the statement will query. &lt;/p&gt;

&lt;p&gt;Generally, SQL queries follow this syntax:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;SELECT &lt;span class="highlight"&gt;column_to_select&lt;/span&gt; FROM &lt;span class="highlight"&gt;table_to_select&lt;/span&gt; WHERE &lt;span class="highlight"&gt;certain_conditions_apply&lt;/span&gt;;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By way of example, the following statement will return the entire &lt;code&gt;name&lt;/code&gt; column from the &lt;code&gt;dinners&lt;/code&gt; table:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;SELECT name FROM dinners;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;+---------+
| name    |
+---------+
| Dolly   |
| Etta    |
| Irma    |
| Barbara |
| Gladys  |
+---------+
5 rows in set (0.00 sec)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can select multiple columns from the same table by separating their names with a comma, like this:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;SELECT name, birthdate FROM dinners;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;+---------+------------+
| name    | birthdate  |
+---------+------------+
| Dolly   | 1946-01-19 |
| Etta    | 1938-01-25 |
| Irma    | 1941-02-18 |
| Barbara | 1948-12-25 |
| Gladys  | 1944-05-28 |
+---------+------------+
5 rows in set (0.00 sec)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Instead of naming a specific column or set of columns, you can follow the &lt;code&gt;SELECT&lt;/code&gt; operator with an asterisk (&lt;code&gt;*&lt;/code&gt;) which serves as a placeholder representing all the columns in a table. The following command returns every column from the &lt;code&gt;tourneys&lt;/code&gt; table:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;SELECT * FROM tourneys;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;+---------+------+------+------+
| name    | wins | best | size |
+---------+------+------+------+
| Dolly   |    7 |  245 |  8.5 |
| Etta    |    4 |  283 |    9 |
| Irma    |    9 |  266 |    7 |
| Barbara |    2 |  197 |  7.5 |
| Gladys  |   13 |  273 |    8 |
+---------+------+------+------+
5 rows in set (0.00 sec)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;WHERE&lt;/code&gt; is used in queries to filter records that meet a specified condition, and any rows that do not meet that condition are eliminated from the result. A &lt;code&gt;WHERE&lt;/code&gt; clause typically follows this syntax:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;. . . WHERE &lt;span class="highlight"&gt;column_name&lt;/span&gt; &lt;span class="highlight"&gt;comparison_operator&lt;/span&gt; &lt;span class="highlight"&gt;value&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The comparison operator in a &lt;code&gt;WHERE&lt;/code&gt; clause defines how the specified column should be compared against the value. Here are some common SQL comparison operators: &lt;/p&gt;

&lt;table&gt;&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operator&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;=&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;tests for equality&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;!=&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;tests for inequality&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;tests for less-than&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;tests for greater-than&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;=&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;tests for less-than or equal-to&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;gt;=&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;tests for greater-than or equal-to&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;BETWEEN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;tests whether a value lies within a given range&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;IN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;tests whether a row's value is contained in a set of specified values&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;EXISTS&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;tests whether rows exist, given the specified conditions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;LIKE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;tests whether a value matches a specified string&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;IS NULL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;tests for &lt;code&gt;NULL&lt;/code&gt; values&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;IS NOT NULL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;tests for all values other than &lt;code&gt;NULL&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;

&lt;p&gt;For example, if you wanted to find Irma's shoe size, you could use the following query:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;SELECT size FROM tourneys WHERE name = 'Irma';
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;+------+
| size |
+------+
|    7 |
+------+
1 row in set (0.00 sec)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;SQL allows the use of wildcard characters, and these are especially handy when used in &lt;code&gt;WHERE&lt;/code&gt; clauses. Percentage signs (&lt;code&gt;%&lt;/code&gt;) represent zero or more unknown characters, and underscores (&lt;code&gt;_&lt;/code&gt;) represent a single unknown character. These are useful if you're trying to find a specific entry in a table, but aren't sure of what that entry is exactly. To illustrate, let's say that you've forgotten the favorite entree of a few of your friends, but you're certain this particular entree starts with a "t." You could find its name by running the following query:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;SELECT entree FROM dinners WHERE entree LIKE 't%';
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;+--------+
| entree |
+--------+
| tofu   |
| tofu   |
+--------+
2 rows in set (0.00 sec)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Based on the output above, we see that the entree we have forgotten is &lt;code&gt;tofu&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;There may be times when you're working with databases that have columns or tables with relatively long or difficult-to-read names. In these cases, you can make these names more readable by creating an alias with the &lt;code&gt;AS&lt;/code&gt; keyword. Aliases created with &lt;code&gt;AS&lt;/code&gt; are temporary, and only exist for the duration of the query for which they're created:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;SELECT name AS n, birthdate AS b, dessert AS d FROM dinners;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;+---------+------------+-----------+
| n       | b          | d         |
+---------+------------+-----------+
| Dolly   | 1946-01-19 | cake      |
| Etta    | 1938-01-25 | ice cream |
| Irma    | 1941-02-18 | cake      |
| Barbara | 1948-12-25 | ice cream |
| Gladys  | 1944-05-28 | ice cream |
+---------+------------+-----------+
5 rows in set (0.00 sec)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, we have told SQL to display the &lt;code&gt;name&lt;/code&gt; column as &lt;code&gt;n&lt;/code&gt;, the &lt;code&gt;birthdate&lt;/code&gt; column as &lt;code&gt;b&lt;/code&gt;, and the &lt;code&gt;dessert&lt;/code&gt; column as &lt;code&gt;d&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;The examples we've gone through up to this point include some of the more frequently-used keywords and clauses in SQL queries. These are useful for basic queries, but they aren't helpful if you're trying to perform a calculation or derive a &lt;em&gt;scalar value&lt;/em&gt; (a single value, as opposed to a set of multiple different values) based on your data. This is where aggregate functions come into play.&lt;/p&gt;

&lt;h2 id="aggregate-functions"&gt;Aggregate Functions&lt;/h2&gt;

&lt;p&gt;Oftentimes, when working with data, you don't necessarily want to see the data itself. Rather, you want information &lt;em&gt;about&lt;/em&gt; the data. The SQL syntax includes a number of functions that allow you to interpret or run calculations on your data just by issuing a &lt;code&gt;SELECT&lt;/code&gt; query. These are known as &lt;em&gt;aggregate functions&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;COUNT&lt;/code&gt; function counts and returns the number of rows that match a certain criteria. For example, if you'd like to know how many of your friends prefer tofu for their birthday entree, you could issue this query:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;SELECT COUNT(entree) FROM dinners WHERE entree = 'tofu';
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;+---------------+
| COUNT(entree) |
+---------------+
|             2 |
+---------------+
1 row in set (0.00 sec)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;AVG&lt;/code&gt; function returns the average (mean) value of a column. Using our example table, you could find the average best score amongst your friends with this query:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;SELECT AVG(best) FROM tourneys;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;+-----------+
| AVG(best) |
+-----------+
|     252.8 |
+-----------+
1 row in set (0.00 sec)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;SUM&lt;/code&gt; is used to find the total sum of a given column. For instance, if you'd like to see how many games you and your friends have bowled over the years, you could run this query:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;SELECT SUM(wins) FROM tourneys;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;+-----------+
| SUM(wins) |
+-----------+
|        35 |
+-----------+
1 row in set (0.00 sec)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that the &lt;code&gt;AVG&lt;/code&gt; and &lt;code&gt;SUM&lt;/code&gt; functions will only work correctly when used with numeric data. If you try to use them on non-numerical data, it will result in either an error or just &lt;code&gt;0&lt;/code&gt;, depending on which RDBMS you're using:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;SELECT SUM(entree) FROM dinners;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;+-------------+
| SUM(entree) |
+-------------+
|           0 |
+-------------+
1 row in set, 5 warnings (0.00 sec)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;MIN&lt;/code&gt; is used to find the smallest value within a specified column. You could use this query to see what the worst overall bowling record is so far (in terms of number of wins):&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;SELECT MIN(wins) FROM tourneys;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;+-----------+
| MIN(wins) |
+-----------+
|         2 |
+-----------+
1 row in set (0.00 sec)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Similarly, &lt;code&gt;MAX&lt;/code&gt; is used to find the largest numeric value in a given column. The following query will show the best overall bowling record:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;SELECT MAX(wins) FROM tourneys;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;+-----------+
| MAX(wins) |
+-----------+
|        13 |
+-----------+
1 row in set (0.00 sec)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Unlike &lt;code&gt;SUM&lt;/code&gt; and &lt;code&gt;AVG&lt;/code&gt;, the &lt;code&gt;MIN&lt;/code&gt; and &lt;code&gt;MAX&lt;/code&gt; functions can be used for both numeric and alphabetic data types. When run on a column containing string values, the &lt;code&gt;MIN&lt;/code&gt; function will show the first value alphabetically: &lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;SELECT MIN(name) FROM dinners;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;+-----------+
| MIN(name) |
+-----------+
| Barbara   |
+-----------+
1 row in set (0.00 sec)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Likewise, when run on a column containing string values, the &lt;code&gt;MAX&lt;/code&gt; function will show the last value alphabetically:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;SELECT MAX(name) FROM dinners;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;+-----------+
| MAX(name) |
+-----------+
| Irma      |
+-----------+
1 row in set (0.00 sec)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Aggregate functions have many uses beyond what was described in this section. They're particularly useful when used with the &lt;code&gt;GROUP BY&lt;/code&gt; clause, which is covered in the next section along with several other query clauses that affect how result-sets are sorted.&lt;/p&gt;

&lt;h2 id="manipulating-query-outputs"&gt;Manipulating Query Outputs&lt;/h2&gt;

&lt;p&gt;In addition to the &lt;code&gt;FROM&lt;/code&gt; and &lt;code&gt;WHERE&lt;/code&gt; clauses, there are several other clauses which are used to manipulate the results of a &lt;code&gt;SELECT&lt;/code&gt; query. In this section, we will explain and provide examples for some of the more commonly-used query clauses.&lt;/p&gt;

&lt;p&gt;One of the most frequently-used query clauses, aside from &lt;code&gt;FROM&lt;/code&gt; and &lt;code&gt;WHERE&lt;/code&gt;, is the &lt;code&gt;GROUP BY&lt;/code&gt; clause. It's typically used when you're performing an aggregate function on one column, but in relation to matching values in another. &lt;/p&gt;

&lt;p&gt;For example, let's say you wanted to know how many of your friends prefer each of the three entrees you make. You could find this info with the following query:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;SELECT COUNT(name), entree FROM dinners GROUP BY entree;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;+-------------+---------+
| COUNT(name) | entree  |
+-------------+---------+
|           1 | chicken |
|           2 | steak   |
|           2 | tofu    |
+-------------+---------+
3 rows in set (0.00 sec)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;ORDER BY&lt;/code&gt; clause is used to sort query results. By default, numeric values are sorted in ascending order, and text values are sorted in alphabetical order. To illustrate, the following query lists the &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;birthdate&lt;/code&gt; columns, but sorts the results by birthdate:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;SELECT name, birthdate FROM dinners ORDER BY birthdate;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;+---------+------------+
| name    | birthdate  |
+---------+------------+
| Etta    | 1938-01-25 |
| Irma    | 1941-02-18 |
| Gladys  | 1944-05-28 |
| Dolly   | 1946-01-19 |
| Barbara | 1948-12-25 |
+---------+------------+
5 rows in set (0.00 sec)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notice that the default behavior of &lt;code&gt;ORDER BY&lt;/code&gt; is to sort the result-set in ascending order. To reverse this and have the result-set sorted in descending order, close the query with &lt;code&gt;DESC&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;SELECT name, birthdate FROM dinners ORDER BY birthdate DESC;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;+---------+------------+
| name    | birthdate  |
+---------+------------+
| Barbara | 1948-12-25 |
| Dolly   | 1946-01-19 |
| Gladys  | 1944-05-28 |
| Irma    | 1941-02-18 |
| Etta    | 1938-01-25 |
+---------+------------+
5 rows in set (0.00 sec)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As mentioned previously, the &lt;code&gt;WHERE&lt;/code&gt; clause is used to filter results based on specific conditions. However, if you use the &lt;code&gt;WHERE&lt;/code&gt; clause with an aggregate function, it will return an error, as is the case with the following attempt to find which sides are the favorite of at least three of your friends:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;SELECT COUNT(name), side FROM dinners WHERE COUNT(name) &amp;gt;= 3;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;ERROR 1111 (HY000): Invalid use of group function
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;HAVING&lt;/code&gt; clause was added to SQL to provide functionality similar to that of the &lt;code&gt;WHERE&lt;/code&gt; clause while also being compatible with aggregate functions. It's helpful to think of the difference between these two clauses as being that &lt;code&gt;WHERE&lt;/code&gt; applies to individual records, while &lt;code&gt;HAVING&lt;/code&gt; applies to group records. To this end, any time you issue a &lt;code&gt;HAVING&lt;/code&gt; clause, the &lt;code&gt;GROUP BY&lt;/code&gt; clause must also be present.&lt;/p&gt;

&lt;p&gt;The following example is another attempt to find which side dishes are the favorite of at least three of your friends, although this one will return a result without error:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;SELECT COUNT(name), side FROM dinners GROUP BY side HAVING COUNT(name) &amp;gt;= 3;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;+-------------+-------+
| COUNT(name) | side  |
+-------------+-------+
|           3 | fries |
+-------------+-------+
1 row in set (0.00 sec)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Aggregate functions are useful for summarizing the results of a particular column in a given table. However, there are many cases where it's necessary to query the contents of more than one table. We'll go over a few ways you can do this in the next section.&lt;/p&gt;

&lt;h2 id="querying-multiple-tables"&gt;Querying Multiple Tables&lt;/h2&gt;

&lt;p&gt;More often than not, a database contains multiple tables, each holding different sets of data. SQL provides a few different ways to run a single query on multiple tables.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;JOIN&lt;/code&gt; clause can be used to combine rows from two or more tables in a query result. It does this by finding a related column between the tables and sorts the results appropriately in the output.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;SELECT&lt;/code&gt; statements that include a &lt;code&gt;JOIN&lt;/code&gt; clause generally follow this syntax:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;SELECT &lt;span class="highlight"&gt;table1&lt;/span&gt;.&lt;span class="highlight"&gt;column1&lt;/span&gt;, &lt;span class="highlight"&gt;table2&lt;/span&gt;.&lt;span class="highlight"&gt;column2&lt;/span&gt;
&lt;/li&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;FROM &lt;span class="highlight"&gt;table1&lt;/span&gt;
&lt;/li&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;JOIN &lt;span class="highlight"&gt;table2&lt;/span&gt; ON &lt;span class="highlight"&gt;table1&lt;/span&gt;.&lt;span class="highlight"&gt;related_column&lt;/span&gt;=&lt;span class="highlight"&gt;table2&lt;/span&gt;.&lt;span class="highlight"&gt;related_column&lt;/span&gt;;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that because &lt;code&gt;JOIN&lt;/code&gt; clauses compare the contents of more than one table, the previous example specifies which table to select each column from by preceding the name of the column with the name of the table and a period. You can specify which table a column should be selected from like this for any query, although it's not necessary when selecting from a single table, as we've done in the previous sections. Let's walk through an example using our sample data. &lt;/p&gt;

&lt;p&gt;Imagine that you wanted to buy each of your friends a pair of bowling shoes as a birthday gift. Because the information about your friends' birthdates and shoe sizes are held in separate tables, you could query both tables separately then compare the results from each. With a &lt;code&gt;JOIN&lt;/code&gt; clause, though, you can find all the information you want with a single query:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;SELECT tourneys.name, tourneys.size, dinners.birthdate 
&lt;/li&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;FROM tourneys 
&lt;/li&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;JOIN dinners ON tourneys.name=dinners.name;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;+---------+------+------------+
| name    | size | birthdate  |
+---------+------+------------+
| Dolly   |  8.5 | 1946-01-19 |
| Etta    |    9 | 1938-01-25 |
| Irma    |    7 | 1941-02-18 |
| Barbara |  7.5 | 1948-12-25 |
| Gladys  |    8 | 1944-05-28 |
+---------+------+------------+
5 rows in set (0.00 sec)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;JOIN&lt;/code&gt; clause used in this example, without any other arguments, is an &lt;em&gt;inner&lt;/em&gt; &lt;code&gt;JOIN&lt;/code&gt; clause. This means that it selects all the records that have matching values in both tables and prints them to the results set, while any records that aren't matched are excluded. To illustrate this idea, let's add a new row to each table that doesn't have a corresponding entry in the other:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;INSERT INTO tourneys (name, wins, best, size) 
&lt;/li&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;VALUES ('Bettye', '0', '193', '9');
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;INSERT INTO dinners (name, birthdate, entree, side, dessert) 
&lt;/li&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;VALUES ('Lesley', '1946-05-02', 'steak', 'salad', 'ice cream');
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, re-run the previous &lt;code&gt;SELECT&lt;/code&gt; statement with the &lt;code&gt;JOIN&lt;/code&gt; clause:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;SELECT tourneys.name, tourneys.size, dinners.birthdate 
&lt;/li&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;FROM tourneys 
&lt;/li&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;JOIN dinners ON tourneys.name=dinners.name;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;+---------+------+------------+
| name    | size | birthdate  |
+---------+------+------------+
| Dolly   |  8.5 | 1946-01-19 |
| Etta    |    9 | 1938-01-25 |
| Irma    |    7 | 1941-02-18 |
| Barbara |  7.5 | 1948-12-25 |
| Gladys  |    8 | 1944-05-28 |
+---------+------+------------+
5 rows in set (0.00 sec)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notice that, because the &lt;code&gt;tourneys&lt;/code&gt; table has no entry for Lesley and the &lt;code&gt;dinners&lt;/code&gt; table has no entry for Bettye, those records are absent from this output.&lt;/p&gt;

&lt;p&gt;It is possible, though, to return all the records from one of the tables using an &lt;em&gt;outer&lt;/em&gt; &lt;code&gt;JOIN&lt;/code&gt; clause. In MySQL, &lt;code&gt;JOIN&lt;/code&gt; clauses are written as either &lt;code&gt;LEFT JOIN&lt;/code&gt; or &lt;code&gt;RIGHT JOIN&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;A &lt;code&gt;LEFT JOIN&lt;/code&gt; clause returns all the records from the “left” table and only the matching records from the right table. In the context of outer joins, the left table is the one referenced by the &lt;code&gt;FROM&lt;/code&gt; clause, and the right table is any other table referenced after the &lt;code&gt;JOIN&lt;/code&gt; statement. &lt;/p&gt;

&lt;p&gt;Run the previous query again, but this time use a &lt;code&gt;LEFT JOIN&lt;/code&gt; clause:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;SELECT tourneys.name, tourneys.size, dinners.birthdate 
&lt;/li&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;FROM tourneys 
&lt;/li&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;LEFT JOIN dinners ON tourneys.name=dinners.name;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This command will return every record from the left table (in this case, &lt;code&gt;tourneys&lt;/code&gt;) even if it doesn't have a corresponding record in the right table. Any time there isn't a matching record from the right table, it's returned as &lt;code&gt;NULL&lt;/code&gt; or just a blank value, depending on your RDBMS:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;+---------+------+------------+
| name    | size | birthdate  |
+---------+------+------------+
| Dolly   |  8.5 | 1946-01-19 |
| Etta    |    9 | 1938-01-25 |
| Irma    |    7 | 1941-02-18 |
| Barbara |  7.5 | 1948-12-25 |
| Gladys  |    8 | 1944-05-28 |
| Bettye  |    9 | NULL       |
+---------+------+------------+
6 rows in set (0.00 sec)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now run the query again, this time with a &lt;code&gt;RIGHT JOIN&lt;/code&gt; clause:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;SELECT tourneys.name, tourneys.size, dinners.birthdate 
&lt;/li&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;FROM tourneys 
&lt;/li&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;RIGHT JOIN dinners ON tourneys.name=dinners.name;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will return all the records from the right table (&lt;code&gt;dinners&lt;/code&gt;). Because Lesley's birthdate is recorded in the right table, but there is no corresponding row for her in the left table, the &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;size&lt;/code&gt; columns will return as &lt;code&gt;NULL&lt;/code&gt; values in that row:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;+---------+------+------------+
| name    | size | birthdate  |
+---------+------+------------+
| Dolly   |  8.5 | 1946-01-19 |
| Etta    |    9 | 1938-01-25 |
| Irma    |    7 | 1941-02-18 |
| Barbara |  7.5 | 1948-12-25 |
| Gladys  |    8 | 1944-05-28 |
| NULL    | NULL | 1946-05-02 |
+---------+------+------------+
6 rows in set (0.00 sec)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that left and right joins can be written as &lt;code&gt;LEFT OUTER JOIN&lt;/code&gt; or &lt;code&gt;RIGHT OUTER JOIN&lt;/code&gt;, although the &lt;code&gt;OUTER&lt;/code&gt; part of the clause is implied. Likewise, specifying &lt;code&gt;INNER JOIN&lt;/code&gt; will produce the same result as just writing &lt;code&gt;JOIN&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;As an alternative to using &lt;code&gt;JOIN&lt;/code&gt; to query records from multiple tables, you can use the &lt;code&gt;UNION&lt;/code&gt; clause.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;UNION&lt;/code&gt; operator works slightly differently than a &lt;code&gt;JOIN&lt;/code&gt; clause: instead of printing results from multiple tables as unique columns using a single &lt;code&gt;SELECT&lt;/code&gt; statement, &lt;code&gt;UNION&lt;/code&gt; combines the results of two &lt;code&gt;SELECT&lt;/code&gt; statements into a single column.&lt;/p&gt;

&lt;p&gt;To illustrate, run the following query:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;SELECT name FROM tourneys UNION SELECT name FROM dinners;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This query will remove any duplicate entries, which is the default behavior of the &lt;code&gt;UNION&lt;/code&gt; operator:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;+---------+
| name    |
+---------+
| Dolly   |
| Etta    |
| Irma    |
| Barbara |
| Gladys  |
| Bettye  |
| Lesley  |
+---------+
7 rows in set (0.00 sec)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To return all entries (including duplicates) use the &lt;code&gt;UNION ALL&lt;/code&gt; operator:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;SELECT name FROM tourneys UNION ALL SELECT name FROM dinners;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;+---------+
| name    |
+---------+
| Dolly   |
| Etta    |
| Irma    |
| Barbara |
| Gladys  |
| Bettye  |
| Dolly   |
| Etta    |
| Irma    |
| Barbara |
| Gladys  |
| Lesley  |
+---------+
12 rows in set (0.00 sec)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The names and number of the columns in the results table reflect the name and number of columns queried by the first &lt;code&gt;SELECT&lt;/code&gt; statement. Note that when using &lt;code&gt;UNION&lt;/code&gt; to query multiple columns from more than one table, each &lt;code&gt;SELECT&lt;/code&gt; statement must query the same number of columns, the respective columns must have similar data types, and the columns in each &lt;code&gt;SELECT&lt;/code&gt; statement must be in the same order. The following example shows what might result if you use a &lt;code&gt;UNION&lt;/code&gt; clause on two &lt;code&gt;SELECT&lt;/code&gt; statements that query a different number of columns:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;SELECT name FROM dinners UNION SELECT name, wins FROM tourneys;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;ERROR 1222 (21000): The used SELECT statements have a different number of columns
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Another way to query multiple tables is through the use of &lt;em&gt;subqueries&lt;/em&gt;. Subqueries (also known as &lt;em&gt;inner&lt;/em&gt; or &lt;em&gt;nested queries&lt;/em&gt;) are queries enclosed within another query. These are useful in cases where you're trying to filter the results of a query against the result of a separate aggregate function.&lt;/p&gt;

&lt;p&gt;To illustrate this idea, say you want to know which of your friends have won more matches than Barbara. Rather than querying how many matches Barbara has won then running another query to see who has won more games than that, you can calculate both with a single query:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;SELECT name, wins FROM tourneys 
&lt;/li&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;WHERE wins &amp;gt; (
&lt;/li&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;SELECT wins FROM tourneys WHERE name = 'Barbara'
&lt;/li&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;);
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;+--------+------+
| name   | wins |
+--------+------+
| Dolly  |    7 |
| Etta   |    4 |
| Irma   |    9 |
| Gladys |   13 |
+--------+------+
4 rows in set (0.00 sec)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The subquery in this statement was run only once; it only needed to find the value from the &lt;code&gt;wins&lt;/code&gt; column in the same row as &lt;code&gt;Barbara&lt;/code&gt; in the &lt;code&gt;name&lt;/code&gt; column, and the data returned by the subquery and outer query are independent of one another. There are cases, though, where the outer query must first read every row in a table and compare those values against the data returned by the subquery in order to return the desired data. In this case, the subquery is referred to as a &lt;em&gt;correlated subquery&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The following statement is an example of a correlated subquery. This query seeks to find which of your friends have won more games than is the average for those with the same shoe size:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;SELECT name, size FROM tourneys AS t 
&lt;/li&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;WHERE wins &amp;gt; (
&lt;/li&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;SELECT AVG(wins) FROM tourneys WHERE size = t.size
&lt;/li&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;);
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In order for the query to complete, it must first collect the &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;size&lt;/code&gt; columns from the outer query. Then, it compares each row from that result set against the results of the inner query, which determines the average number of wins for individuals with identical shoe sizes. Because you only have two friends that have the same shoe size, there can only be one row in the result-set:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;+------+------+
| name | size |
+------+------+
| Etta |    9 |
+------+------+
1 row in set (0.00 sec)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As mentioned earlier, subqueries can be used to query results from multiple tables. To illustrate this with one final example, say you wanted to throw a surprise dinner for the group's all-time best bowler. You could find which of your friends has the best bowling record and return their favorite meal with the following query:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;SELECT name, entree, side, dessert 
&lt;/li&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;FROM dinners 
&lt;/li&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;WHERE name = (SELECT name FROM tourneys 
&lt;/li&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;WHERE wins = (SELECT MAX(wins) FROM tourneys));
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;+--------+--------+-------+-----------+
| name   | entree | side  | dessert   |
+--------+--------+-------+-----------+
| Gladys | steak  | fries | ice cream |
+--------+--------+-------+-----------+
1 row in set (0.00 sec)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notice that this statement not only includes a subquery, but also contains a subquery within that subquery. &lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Issuing queries is one of the most commonly-performed tasks within the realm of database management. There are a number of database administration tools, such as &lt;a href="https://www.phpmyadmin.net/"&gt;phpMyAdmin&lt;/a&gt; or &lt;a href="https://www.pgadmin.org/"&gt;pgAdmin&lt;/a&gt;, that allow you to perform queries and visualize the results, but issuing &lt;code&gt;SELECT&lt;/code&gt; statements from the command line is still a widely-practiced workflow that can also provide you with greater control.&lt;/p&gt;

&lt;p&gt;If you're new to working with SQL, we encourage you to use our &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-manage-sql-database-cheat-sheet"&gt;SQL Cheat Sheet&lt;/a&gt; as a reference and to review the &lt;a href="https://dev.mysql.com/doc/refman/5.7/en/"&gt;official MySQL documentation&lt;/a&gt;. Additionally, if you'd like to learn more about SQL and relational databases, the following tutorials may be of interest to you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.digitalocean.com/community/tutorials/understanding-sql-and-nosql-databases-and-different-database-models"&gt;Understanding SQL And NoSQL Databases And Different Database Models&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.digitalocean.com/community/tutorials/how-to-create-a-multi-node-mysql-cluster-on-ubuntu-18-04"&gt;How To Create a Multi-Node MySQL Cluster on Ubuntu 18.04&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.digitalocean.com/community/tutorials/how-to-reset-your-mysql-or-mariadb-root-password-on-ubuntu-18-04"&gt;How To Reset Your MySQL or MariaDB Root Password on Ubuntu 18.04&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/introduction-to-queries-postgresql</id>
    <published>2018-10-17T18:50:23Z</published>
    <updated>2018-10-17T20:52:07Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/introduction-to-queries-postgresql"/>
    <title>An Introduction to Queries in PostgreSQL</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;Databases are a key component of many websites and applications, and are at the core of how data is stored and exchanged across the internet. One of the most important aspects of database management is the practice of retrieving data from a database, whether it's on an ad hoc basis or part of a process that's been coded into an application. There are several ways to retrieve information from a database, but one of the most commonly-used methods is performed through submitting &lt;em&gt;queries&lt;/em&gt; through the command line. &lt;/p&gt;

&lt;p&gt;In relational database management systems, a &lt;em&gt;query&lt;/em&gt; is any command used to retrieve data from a table. In Structured Query Language (SQL), queries are almost always made using the &lt;code&gt;SELECT&lt;/code&gt; statement. &lt;/p&gt;

&lt;p&gt;In this guide, we will discuss the basic syntax of SQL queries as well as some of the more commonly-employed functions and operators. We will also practice making SQL queries using some sample data in a PostgreSQL database.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.postgresql.org/"&gt;PostgreSQL&lt;/a&gt;, often shortened to "Postgres," is a relational database management system with an object-oriented approach, meaning that information can be represented as objects or classes in PostgreSQL schemas. PostgreSQL aligns closely with standard SQL, although it also includes some features not found in other relational database systems.&lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;In general, the commands and concepts presented in this guide can be used on any Linux-based operating system running any SQL database software. However, it was written specifically with an Ubuntu 18.04 server running PostgreSQL in mind. To set this up, you will need the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An Ubuntu 18.04 machine with a non-root user with sudo privileges. This can be set up using our &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-18-04"&gt;Initial Server Setup guide for Ubuntu 18.04&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;PostgreSQL installed on the machine. For help with setting this up, follow the "Installing PostgreSQL" section of our guide on &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-and-use-postgresql-on-ubuntu-18-04#installing-postgresql"&gt;How To Install and Use PostgreSQL on Ubuntu 18.04&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With this setup in place, we can begin the tutorial.&lt;/p&gt;

&lt;h2 id="creating-a-sample-database"&gt;Creating a Sample Database&lt;/h2&gt;

&lt;p&gt;Before we can begin making queries in SQL, we will first create a database and a couple tables, then populate these tables with some sample data. This will allow you to gain some hands-on experience when you begin making queries later on.&lt;/p&gt;

&lt;p&gt;For the sample database we'll use throughout this guide, imagine the following scenario:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;You and several of your friends all celebrate your birthdays with one another. On each occasion, the members of the group head to the local bowling alley, participate in a friendly tournament, and then everyone heads to your place where you prepare the birthday-person's favorite meal.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Now that this tradition has been going on for a while, you've decided to begin tracking the records from these tournaments. Also, to make planning dinners easier, you decide to create a record of your friends' birthdays and their favorite entrees, sides, and desserts. Rather than keep this information in a physical ledger, you decide to exercise your database skills by recording it in a PostgreSQL database.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;To begin, open up a PostgreSQL prompt as your &lt;strong&gt;postgres&lt;/strong&gt; superuser:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo -u postgres psql
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;span class='note'&gt;&lt;p&gt;
&lt;strong&gt;Note:&lt;/strong&gt; If you followed all the steps of the prerequisite tutorial on &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-and-use-postgresql-on-ubuntu-18-04"&gt;Installing PostgreSQL on Ubuntu 18.04&lt;/a&gt;, you may have configured a new role for your PostgreSQL installation. In this case, you can connect to the Postgres prompt with the following command, substituting &lt;code&gt;&lt;span class="highlight"&gt;sammy&lt;/span&gt;&lt;/code&gt; with your own username:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo -u &lt;span class="highlight"&gt;sammy&lt;/span&gt; psql
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;/p&gt;&lt;/span&gt;

&lt;p&gt;Next, create the database by running:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="postgres=#"&gt;CREATE DATABASE birthdays;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then select this database by typing:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="postgres=#"&gt;\c birthdays
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, create two tables within this database. We'll use the first table to track your friends' records at the bowling alley. The following command will create a table called &lt;code&gt;tourneys&lt;/code&gt; with columns for the &lt;code&gt;name&lt;/code&gt; of each of your friends, the number of tournaments they've won (&lt;code&gt;wins&lt;/code&gt;), their all-time &lt;code&gt;best&lt;/code&gt; score, and what size bowling shoe they wear (&lt;code&gt;size&lt;/code&gt;):&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="birthdays=#"&gt;CREATE TABLE tourneys ( 
&lt;/li&gt;&lt;li class="line" prefix="birthdays=#"&gt;name varchar(30), 
&lt;/li&gt;&lt;li class="line" prefix="birthdays=#"&gt;wins real, 
&lt;/li&gt;&lt;li class="line" prefix="birthdays=#"&gt;best real, 
&lt;/li&gt;&lt;li class="line" prefix="birthdays=#"&gt;size real 
&lt;/li&gt;&lt;li class="line" prefix="birthdays=#"&gt;);
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once you run the &lt;code&gt;CREATE TABLE&lt;/code&gt; command and populate it with column headings, you’ll receive the following output:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;CREATE TABLE
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Populate the &lt;code&gt;tourneys&lt;/code&gt; table with some sample data:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="birthdays=#"&gt;INSERT INTO tourneys (name, wins, best, size) 
&lt;/li&gt;&lt;li class="line" prefix="birthdays=#"&gt;VALUES ('Dolly', '7', '245', '8.5'), 
&lt;/li&gt;&lt;li class="line" prefix="birthdays=#"&gt;('Etta', '4', '283', '9'), 
&lt;/li&gt;&lt;li class="line" prefix="birthdays=#"&gt;('Irma', '9', '266', '7'), 
&lt;/li&gt;&lt;li class="line" prefix="birthdays=#"&gt;('Barbara', '2', '197', '7.5'), 
&lt;/li&gt;&lt;li class="line" prefix="birthdays=#"&gt;('Gladys', '13', '273', '8');
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You’ll receive the following output:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;INSERT 0 5
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Following this, create another table within the same database which we'll use to store information about your friends' favorite birthday meals. The following command creates a table named &lt;code&gt;dinners&lt;/code&gt; with columns for the &lt;code&gt;name&lt;/code&gt; of each of your friends, their &lt;code&gt;birthdate&lt;/code&gt;, their favorite &lt;code&gt;entree&lt;/code&gt;, their preferred &lt;code&gt;side&lt;/code&gt; dish, and their favorite &lt;code&gt;dessert&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="birthdays=#"&gt;CREATE TABLE dinners ( 
&lt;/li&gt;&lt;li class="line" prefix="birthdays=#"&gt;name varchar(30), 
&lt;/li&gt;&lt;li class="line" prefix="birthdays=#"&gt;birthdate date, 
&lt;/li&gt;&lt;li class="line" prefix="birthdays=#"&gt;entree varchar(30), 
&lt;/li&gt;&lt;li class="line" prefix="birthdays=#"&gt;side varchar(30), 
&lt;/li&gt;&lt;li class="line" prefix="birthdays=#"&gt;dessert varchar(30) 
&lt;/li&gt;&lt;li class="line" prefix="birthdays=#"&gt;);
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Similarly for this table, you’ll receive feedback verifying that the table was created:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;CREATE TABLE
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Populate this table with some sample data as well:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="birthdays=#"&gt;INSERT INTO dinners (name, birthdate, entree, side, dessert) 
&lt;/li&gt;&lt;li class="line" prefix="birthdays=#"&gt;VALUES ('Dolly', '1946-01-19', 'steak', 'salad', 'cake'), 
&lt;/li&gt;&lt;li class="line" prefix="birthdays=#"&gt;('Etta', '1938-01-25', 'chicken', 'fries', 'ice cream'), 
&lt;/li&gt;&lt;li class="line" prefix="birthdays=#"&gt;('Irma', '1941-02-18', 'tofu', 'fries', 'cake'), 
&lt;/li&gt;&lt;li class="line" prefix="birthdays=#"&gt;('Barbara', '1948-12-25', 'tofu', 'salad', 'ice cream'), 
&lt;/li&gt;&lt;li class="line" prefix="birthdays=#"&gt;('Gladys', '1944-05-28', 'steak', 'fries', 'ice cream');
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;INSERT 0 5
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once that command completes successfully, you're done setting up your database. Next, we'll go over the basic command structure of &lt;code&gt;SELECT&lt;/code&gt; queries.&lt;/p&gt;

&lt;h2 id="understanding-select-statements"&gt;Understanding SELECT Statements&lt;/h2&gt;

&lt;p&gt;As mentioned in the introduction, SQL queries almost always begin with the &lt;code&gt;SELECT&lt;/code&gt; statement. &lt;code&gt;SELECT&lt;/code&gt; is used in queries to specify which columns from a table should be returned in the result-set. Queries also almost always include &lt;code&gt;FROM&lt;/code&gt;, which is used to specify which table the statement will query. &lt;/p&gt;

&lt;p&gt;Generally, SQL queries follow this syntax:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="postgres=#"&gt;SELECT &lt;span class="highlight"&gt;column_to_select&lt;/span&gt; FROM &lt;span class="highlight"&gt;table_to_select&lt;/span&gt; WHERE &lt;span class="highlight"&gt;certain_conditions_apply&lt;/span&gt;;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By way of example, the following statement will return the entire &lt;code&gt;name&lt;/code&gt; column from the &lt;code&gt;dinners&lt;/code&gt; table:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="birthdays=#"&gt;SELECT name FROM dinners;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;  name   
---------
 Dolly
 Etta
 Irma
 Barbara
 Gladys
(5 rows)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can select multiple columns from the same table by separating their names with a comma, like this:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="birthdays=#"&gt;SELECT name, birthdate FROM dinners;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;  name   | birthdate  
---------+------------
 Dolly   | 1946-01-19
 Etta    | 1938-01-25
 Irma    | 1941-02-18
 Barbara | 1948-12-25
 Gladys  | 1944-05-28
(5 rows)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Instead of naming a specific column or set of columns, you can follow the &lt;code&gt;SELECT&lt;/code&gt; operator with an asterisk (&lt;code&gt;*&lt;/code&gt;) which serves as a placeholder representing all the columns in a table. The following command returns every column from the &lt;code&gt;tourneys&lt;/code&gt; table:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="birthdays=#"&gt;SELECT * FROM tourneys;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;  name   | wins | best | size 
---------+------+------+------
 Dolly   |    7 |  245 |  8.5
 Etta    |    4 |  283 |    9
 Irma    |    9 |  266 |    7
 Barbara |    2 |  197 |  7.5
 Gladys  |   13 |  273 |    8
(5 rows)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;WHERE&lt;/code&gt; is used in queries to filter records that meet a specified condition, and any rows that do not meet that condition are eliminated from the result. A &lt;code&gt;WHERE&lt;/code&gt; clause typically follows this syntax:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="postgres=#"&gt;. . . WHERE &lt;span class="highlight"&gt;column_name&lt;/span&gt; &lt;span class="highlight"&gt;comparison_operator&lt;/span&gt; &lt;span class="highlight"&gt;value&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The comparison operator in a &lt;code&gt;WHERE&lt;/code&gt; clause defines how the specified column should be compared against the value. Here are some common SQL comparison operators:&lt;/p&gt;

&lt;table&gt;&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operator&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;=&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;tests for equality&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;!=&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;tests for inequality&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;tests for less-than&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;tests for greater-than&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;=&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;tests for less-than or equal-to&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;gt;=&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;tests for greater-than or equal-to&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;BETWEEN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;tests whether a value lies within a given range&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;IN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;tests whether a row's value is contained in a set of specified values&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;EXISTS&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;tests whether rows exist, given the specified conditions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;LIKE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;tests whether a value matches a specified string&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;IS NULL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;tests for &lt;code&gt;NULL&lt;/code&gt; values&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;IS NOT NULL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;tests for all values other than &lt;code&gt;NULL&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;

&lt;p&gt;For example, if you wanted to find Irma's shoe size, you could use the following query:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="birthdays=#"&gt;SELECT size FROM tourneys WHERE name = 'Irma';
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt; size 
------
    7
(1 row)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;SQL allows the use of wildcard characters, and these are especially handy when used in &lt;code&gt;WHERE&lt;/code&gt; clauses. Percentage signs (&lt;code&gt;%&lt;/code&gt;) represent zero or more unknown characters, and underscores (&lt;code&gt;_&lt;/code&gt;) represent a single unknown character. These are useful if you're trying to find a specific entry in a table, but aren't sure of what that entry is exactly. To illustrate, let's say that you've forgotten the favorite entree of a few of your friends, but you're certain this particular entree starts with a "t." You could find its name by running the following query:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="birthdays=#"&gt;SELECT entree FROM dinners WHERE entree LIKE 't%';
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt; entree  
-------
 tofu
 tofu
(2 rows)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Based on the output above, we see that the entree we have forgotten is &lt;code&gt;tofu&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;There may be times when you're working with databases that have columns or tables with relatively long or difficult-to-read names. In these cases, you can make these names more readable by creating an alias with the &lt;code&gt;AS&lt;/code&gt; keyword. Aliases created with &lt;code&gt;AS&lt;/code&gt; are temporary, and only exist for the duration of the query for which they're created:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="birthdays=#"&gt;SELECT name AS n, birthdate AS b, dessert AS d FROM dinners;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;    n    |     b      |     d     
---------+------------+-----------
 Dolly   | 1946-01-19 | cake
 Etta    | 1938-01-25 | ice cream
 Irma    | 1941-02-18 | cake
 Barbara | 1948-12-25 | ice cream
 Gladys  | 1944-05-28 | ice cream
(5 rows)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, we have told SQL to display the &lt;code&gt;name&lt;/code&gt; column as &lt;code&gt;n&lt;/code&gt;, the &lt;code&gt;birthdate&lt;/code&gt; column as &lt;code&gt;b&lt;/code&gt;, and the &lt;code&gt;dessert&lt;/code&gt; column as &lt;code&gt;d&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;The examples we've gone through up to this point include some of the more frequently-used keywords and clauses in SQL queries. These are useful for basic queries, but they aren't helpful if you're trying to perform a calculation or derive a &lt;em&gt;scalar value&lt;/em&gt; (a single value, as opposed to a set of multiple different values) based on your data. This is where aggregate functions come into play.&lt;/p&gt;

&lt;h2 id="aggregate-functions"&gt;Aggregate Functions&lt;/h2&gt;

&lt;p&gt;Oftentimes, when working with data, you don't necessarily want to see the data itself. Rather, you want information &lt;em&gt;about&lt;/em&gt; the data. The SQL syntax includes a number of functions that allow you to interpret or run calculations on your data just by issuing a &lt;code&gt;SELECT&lt;/code&gt; query. These are known as &lt;em&gt;aggregate functions&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;COUNT&lt;/code&gt; function counts and returns the number of rows that match a certain criteria. For example, if you'd like to know how many of your friends prefer tofu for their birthday entree, you could issue this query:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="birthdays=#"&gt;SELECT COUNT(entree) FROM dinners WHERE entree = 'tofu';
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt; count 
-------
     2
(1 row)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;AVG&lt;/code&gt; function returns the average (mean) value of a column. Using our example table, you could find the average best score amongst your friends with this query:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="birthdays=#"&gt;SELECT AVG(best) FROM tourneys;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;  avg  
-------
 252.8
(1 row)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;SUM&lt;/code&gt; is used to find the total sum of a given column. For instance, if you'd like to see how many games you and your friends have bowled over the years, you could run this query:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="birthdays=#"&gt;SELECT SUM(wins) FROM tourneys;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt; sum 
-----
  35
(1 row)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that the &lt;code&gt;AVG&lt;/code&gt; and &lt;code&gt;SUM&lt;/code&gt; functions will only work correctly when used with numeric data. If you try to use them on non-numerical data, it will result in either an error or just &lt;code&gt;0&lt;/code&gt;, depending on which RDBMS you're using:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="birthdays=#"&gt;SELECT SUM(entree) FROM dinners;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;ERROR:  function sum(character varying) does not exist
LINE 1: select sum(entree) from dinners;
               ^
HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;MIN&lt;/code&gt; is used to find the smallest value within a specified column. You could use this query to see what the worst overall bowling record is so far (in terms of number of wins):&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="birthdays=#"&gt;SELECT MIN(wins) FROM tourneys;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt; min 
-----
   2
(1 row)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Similarly, &lt;code&gt;MAX&lt;/code&gt; is used to find the largest numeric value in a given column. The following query will show the best overall bowling record:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="birthdays=#"&gt;SELECT MAX(wins) FROM tourneys;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt; max 
-----
  13
(1 row)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Unlike &lt;code&gt;SUM&lt;/code&gt; and &lt;code&gt;AVG&lt;/code&gt;, the &lt;code&gt;MIN&lt;/code&gt; and &lt;code&gt;MAX&lt;/code&gt; functions can be used for both numeric and alphabetic data types. When run on a column containing string values, the &lt;code&gt;MIN&lt;/code&gt; function will show the first value alphabetically: &lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="birthdays=#"&gt;SELECT MIN(name) FROM dinners;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;   min   
---------
 Barbara
(1 row)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Likewise, when run on a column containing string values, the &lt;code&gt;MAX&lt;/code&gt; function will show the last value alphabetically:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="birthdays=#"&gt;SELECT MAX(name) FROM dinners;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt; max  
------
 Irma
(1 row)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Aggregate functions have many uses beyond what was described in this section. They're particularly useful when used with the &lt;code&gt;GROUP BY&lt;/code&gt; clause, which is covered in the next section along with several other query clauses that affect how result-sets are sorted.&lt;/p&gt;

&lt;h2 id="manipulating-query-outputs"&gt;Manipulating Query Outputs&lt;/h2&gt;

&lt;p&gt;In addition to the &lt;code&gt;FROM&lt;/code&gt; and &lt;code&gt;WHERE&lt;/code&gt; clauses, there are several other clauses which are used to manipulate the results of a &lt;code&gt;SELECT&lt;/code&gt; query. In this section, we will explain and provide examples for some of the more commonly-used query clauses.&lt;/p&gt;

&lt;p&gt;One of the most frequently-used query clauses, aside from &lt;code&gt;FROM&lt;/code&gt; and &lt;code&gt;WHERE&lt;/code&gt;, is the &lt;code&gt;GROUP BY&lt;/code&gt; clause. It's typically used when you're performing an aggregate function on one column, but in relation to matching values in another. &lt;/p&gt;

&lt;p&gt;For example, let's say you wanted to know how many of your friends prefer each of the three entrees you make. You could find this info with the following query:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="birthdays=#"&gt;SELECT COUNT(name), entree FROM dinners GROUP BY entree;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt; count | entree  
-------+---------
     1 | chicken
     2 | steak
     2 | tofu
(3 rows)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;ORDER BY&lt;/code&gt; clause is used to sort query results. By default, numeric values are sorted in ascending order, and text values are sorted in alphabetical order. To illustrate, the following query lists the &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;birthdate&lt;/code&gt; columns, but sorts the results by birthdate:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="birthdays=#"&gt;SELECT name, birthdate FROM dinners ORDER BY birthdate;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;  name   | birthdate  
---------+------------
 Etta    | 1938-01-25
 Irma    | 1941-02-18
 Gladys  | 1944-05-28
 Dolly   | 1946-01-19
 Barbara | 1948-12-25
(5 rows)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notice that the default behavior of &lt;code&gt;ORDER BY&lt;/code&gt; is to sort the result-set in ascending order. To reverse this and have the result-set sorted in descending order, close the query with &lt;code&gt;DESC&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="birthdays=#"&gt;SELECT name, birthdate FROM dinners ORDER BY birthdate DESC;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;  name   | birthdate  
---------+------------
 Barbara | 1948-12-25
 Dolly   | 1946-01-19
 Gladys  | 1944-05-28
 Irma    | 1941-02-18
 Etta    | 1938-01-25
(5 rows)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As mentioned previously, the &lt;code&gt;WHERE&lt;/code&gt; clause is used to filter results based on specific conditions. However, if you use the &lt;code&gt;WHERE&lt;/code&gt; clause with an aggregate function, it will return an error, as is the case with the following attempt to find which sides are the favorite of at least three of your friends:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="birthdays=#"&gt;SELECT COUNT(name), side FROM dinners WHERE COUNT(name) &amp;gt;= 3;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;ERROR:  aggregate functions are not allowed in WHERE
LINE 1: SELECT COUNT(name), side FROM dinners WHERE COUNT(name) &amp;gt;= 3...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;HAVING&lt;/code&gt; clause was added to SQL to provide functionality similar to that of the &lt;code&gt;WHERE&lt;/code&gt; clause while also being compatible with aggregate functions. It's helpful to think of the difference between these two clauses as being that &lt;code&gt;WHERE&lt;/code&gt; applies to individual records, while &lt;code&gt;HAVING&lt;/code&gt; applies to group records. To this end, any time you issue a &lt;code&gt;HAVING&lt;/code&gt; clause, the &lt;code&gt;GROUP BY&lt;/code&gt; clause must also be present.&lt;/p&gt;

&lt;p&gt;The following example is another attempt to find which side dishes are the favorite of at least three of your friends, although this one will return a result without error:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="birthdays=#"&gt;SELECT COUNT(name), side FROM dinners GROUP BY side HAVING COUNT(name) &amp;gt;= 3;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt; count | side  
-------+-------
     3 | fries
(1 row)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Aggregate functions are useful for summarizing the results of a particular column in a given table. However, there are many cases where it's necessary to query the contents of more than one table. We'll go over a few ways you can do this in the next section.&lt;/p&gt;

&lt;h2 id="querying-multiple-tables"&gt;Querying Multiple Tables&lt;/h2&gt;

&lt;p&gt;More often than not, a database contains multiple tables, each holding different sets of data. SQL provides a few different ways to run a single query on multiple tables.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;JOIN&lt;/code&gt; clause can be used to combine rows from two or more tables in a query result. It does this by finding a related column between the tables and sorts the results appropriately in the output.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;SELECT&lt;/code&gt; statements that include a &lt;code&gt;JOIN&lt;/code&gt; clause generally follow this syntax:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="postgres=#"&gt;SELECT &lt;span class="highlight"&gt;table1&lt;/span&gt;.&lt;span class="highlight"&gt;column1&lt;/span&gt;, &lt;span class="highlight"&gt;table2&lt;/span&gt;.&lt;span class="highlight"&gt;column2&lt;/span&gt;
&lt;/li&gt;&lt;li class="line" prefix="postgres=#"&gt;FROM &lt;span class="highlight"&gt;table1&lt;/span&gt;
&lt;/li&gt;&lt;li class="line" prefix="postgres=#"&gt;JOIN &lt;span class="highlight"&gt;table2&lt;/span&gt; ON &lt;span class="highlight"&gt;table1&lt;/span&gt;.&lt;span class="highlight"&gt;related_column&lt;/span&gt;=&lt;span class="highlight"&gt;table2&lt;/span&gt;.&lt;span class="highlight"&gt;related_column&lt;/span&gt;;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that because &lt;code&gt;JOIN&lt;/code&gt; clauses compare the contents of more than one table, the previous example specifies which table to select each column from by preceding the name of the column with the name of the table and a period. You can specify which table a column should be selected from like this for any query, although it's not necessary when selecting from a single table, as we've done in the previous sections. Let's walk through an example using our sample data. &lt;/p&gt;

&lt;p&gt;Imagine that you wanted to buy each of your friends a pair of bowling shoes as a birthday gift. Because the information about your friends' birthdates and shoe sizes are held in separate tables, you could query both tables separately then compare the results from each. With a &lt;code&gt;JOIN&lt;/code&gt; clause, though, you can find all the information you want with a single query:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="birthdays=#"&gt;SELECT tourneys.name, tourneys.size, dinners.birthdate 
&lt;/li&gt;&lt;li class="line" prefix="birthdays=#"&gt;FROM tourneys 
&lt;/li&gt;&lt;li class="line" prefix="birthdays=#"&gt;JOIN dinners ON tourneys.name=dinners.name;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;  name   | size | birthdate  
---------+------+------------
 Dolly   |  8.5 | 1946-01-19
 Etta    |    9 | 1938-01-25
 Irma    |    7 | 1941-02-18
 Barbara |  7.5 | 1948-12-25
 Gladys  |    8 | 1944-05-28
(5 rows)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;JOIN&lt;/code&gt; clause used in this example, without any other arguments, is an &lt;em&gt;inner&lt;/em&gt; &lt;code&gt;JOIN&lt;/code&gt; clause. This means that it selects all the records that have matching values in both tables and prints them to the results set, while any records that aren't matched are excluded. To illustrate this idea, let's add a new row to each table that doesn't have a corresponding entry in the other:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="birthdays=#"&gt;INSERT INTO tourneys (name, wins, best, size) 
&lt;/li&gt;&lt;li class="line" prefix="birthdays=#"&gt;VALUES ('Bettye', '0', '193', '9');
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="birthdays=#"&gt;INSERT INTO dinners (name, birthdate, entree, side, dessert) 
&lt;/li&gt;&lt;li class="line" prefix="birthdays=#"&gt;VALUES ('Lesley', '1946-05-02', 'steak', 'salad', 'ice cream');
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, re-run the previous &lt;code&gt;SELECT&lt;/code&gt; statement with the &lt;code&gt;JOIN&lt;/code&gt; clause:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="birthdays=#"&gt;SELECT tourneys.name, tourneys.size, dinners.birthdate 
&lt;/li&gt;&lt;li class="line" prefix="birthdays=#"&gt;FROM tourneys 
&lt;/li&gt;&lt;li class="line" prefix="birthdays=#"&gt;JOIN dinners ON tourneys.name=dinners.name;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;  name   | size | birthdate  
---------+------+------------
 Dolly   |  8.5 | 1946-01-19
 Etta    |    9 | 1938-01-25
 Irma    |    7 | 1941-02-18
 Barbara |  7.5 | 1948-12-25
 Gladys  |    8 | 1944-05-28
(5 rows)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notice that, because the &lt;code&gt;tourneys&lt;/code&gt; table has no entry for Lesley and the &lt;code&gt;dinners&lt;/code&gt; table has no entry for Bettye, those records are absent from this output.&lt;/p&gt;

&lt;p&gt;It is possible, though, to return all the records from one of the tables using an &lt;em&gt;outer&lt;/em&gt; &lt;code&gt;JOIN&lt;/code&gt; clause. Outer &lt;code&gt;JOIN&lt;/code&gt; clauses are written as either &lt;code&gt;LEFT JOIN&lt;/code&gt;, &lt;code&gt;RIGHT JOIN&lt;/code&gt;, or &lt;code&gt;FULL JOIN&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;A &lt;code&gt;LEFT JOIN&lt;/code&gt; clause returns all the records from the “left” table and only the matching records from the right table. In the context of outer joins, the left table is the one referenced by the &lt;code&gt;FROM&lt;/code&gt; clause, and the right table is any other table referenced after the &lt;code&gt;JOIN&lt;/code&gt; statement. &lt;/p&gt;

&lt;p&gt;Run the previous query again, but this time use a &lt;code&gt;LEFT JOIN&lt;/code&gt; clause:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="birthdays=#"&gt;SELECT tourneys.name, tourneys.size, dinners.birthdate 
&lt;/li&gt;&lt;li class="line" prefix="birthdays=#"&gt;FROM tourneys 
&lt;/li&gt;&lt;li class="line" prefix="birthdays=#"&gt;LEFT JOIN dinners ON tourneys.name=dinners.name;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This command will return every record from the left table (in this case, &lt;code&gt;tourneys&lt;/code&gt;) even if it doesn't have a corresponding record in the right table. Any time there isn't a matching record from the right table, it's returned as a blank value or &lt;code&gt;NULL&lt;/code&gt;, depending on your RDBMS:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;  name   | size | birthdate  
---------+------+------------
 Dolly   |  8.5 | 1946-01-19
 Etta    |    9 | 1938-01-25
 Irma    |    7 | 1941-02-18
 Barbara |  7.5 | 1948-12-25
 Gladys  |    8 | 1944-05-28
 Bettye  |    9 | 
(6 rows)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now run the query again, this time with a &lt;code&gt;RIGHT JOIN&lt;/code&gt; clause:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="birthdays=#"&gt;SELECT tourneys.name, tourneys.size, dinners.birthdate 
&lt;/li&gt;&lt;li class="line" prefix="birthdays=#"&gt;FROM tourneys 
&lt;/li&gt;&lt;li class="line" prefix="birthdays=#"&gt;RIGHT JOIN dinners ON tourneys.name=dinners.name;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will return all the records from the right table (&lt;code&gt;dinners&lt;/code&gt;). Because Lesley's birthdate is recorded in the right table, but there is no corresponding row for her in the left table, the &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;size&lt;/code&gt; columns will return as blank values in that row:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;  name   | size | birthdate  
---------+------+------------
 Dolly   |  8.5 | 1946-01-19
 Etta    |    9 | 1938-01-25
 Irma    |    7 | 1941-02-18
 Barbara |  7.5 | 1948-12-25
 Gladys  |    8 | 1944-05-28
         |      | 1946-05-02
(6 rows)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that left and right joins can be written as &lt;code&gt;LEFT OUTER JOIN&lt;/code&gt; or &lt;code&gt;RIGHT OUTER JOIN&lt;/code&gt;, although the &lt;code&gt;OUTER&lt;/code&gt; part of the clause is implied. Likewise, specifying &lt;code&gt;INNER JOIN&lt;/code&gt; will produce the same result as just writing &lt;code&gt;JOIN&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;There is a fourth join clause called &lt;code&gt;FULL JOIN&lt;/code&gt; available for some RDBMS distributions, including PostgreSQL. A &lt;code&gt;FULL JOIN&lt;/code&gt; will return all the records from each table, including any null values:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="birthdays=#"&gt;SELECT tourneys.name, tourneys.size, dinners.birthdate 
&lt;/li&gt;&lt;li class="line" prefix="birthdays=#"&gt;FROM tourneys 
&lt;/li&gt;&lt;li class="line" prefix="birthdays=#"&gt;FULL JOIN dinners ON tourneys.name=dinners.name;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;  name   | size | birthdate  
---------+------+------------
 Dolly   |  8.5 | 1946-01-19
 Etta    |    9 | 1938-01-25
 Irma    |    7 | 1941-02-18
 Barbara |  7.5 | 1948-12-25
 Gladys  |    8 | 1944-05-28
 Bettye  |    9 | 
         |      | 1946-05-02
(7 rows)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span class='note'&gt;&lt;strong&gt;Note:&lt;/strong&gt; As of this writing, the &lt;code&gt;FULL JOIN&lt;/code&gt; clause is not supported by either MySQL or MariaDB.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;As an alternative to using &lt;code&gt;FULL JOIN&lt;/code&gt; to query all the records from multiple tables, you can use the &lt;code&gt;UNION&lt;/code&gt; clause.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;UNION&lt;/code&gt; operator works slightly differently than a &lt;code&gt;JOIN&lt;/code&gt; clause: instead of printing results from multiple tables as unique columns using a single &lt;code&gt;SELECT&lt;/code&gt; statement, &lt;code&gt;UNION&lt;/code&gt; combines the results of two &lt;code&gt;SELECT&lt;/code&gt; statements into a single column.&lt;/p&gt;

&lt;p&gt;To illustrate, run the following query:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="birthdays=#"&gt;SELECT name FROM tourneys UNION SELECT name FROM dinners;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This query will remove any duplicate entries, which is the default behavior of the &lt;code&gt;UNION&lt;/code&gt; operator:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;  name   
---------
 Irma
 Etta
 Bettye
 Gladys
 Barbara
 Lesley
 Dolly
(7 rows)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To return all entries (including duplicates) use the &lt;code&gt;UNION ALL&lt;/code&gt; operator:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="birthdays=#"&gt;SELECT name FROM tourneys UNION ALL SELECT name FROM dinners;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;  name   
---------
 Dolly
 Etta
 Irma
 Barbara
 Gladys
 Bettye
 Dolly
 Etta
 Irma
 Barbara
 Gladys
 Lesley
(12 rows)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The names and number of the columns in the results table reflect the name and number of columns queried by the first &lt;code&gt;SELECT&lt;/code&gt; statement. Note that when using &lt;code&gt;UNION&lt;/code&gt; to query multiple columns from more than one table, each &lt;code&gt;SELECT&lt;/code&gt; statement must query the same number of columns, the respective columns must have similar data types, and the columns in each &lt;code&gt;SELECT&lt;/code&gt; statement must be in the same order. The following example shows what might result if you use a &lt;code&gt;UNION&lt;/code&gt; clause on two &lt;code&gt;SELECT&lt;/code&gt; statements that query a different number of columns:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="birthdays=#"&gt;SELECT name FROM dinners UNION SELECT name, wins FROM tourneys;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;ERROR:  each UNION query must have the same number of columns
LINE 1: SELECT name FROM dinners UNION SELECT name, wins FROM tourne...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Another way to query multiple tables is through the use of &lt;em&gt;subqueries&lt;/em&gt;. Subqueries (also known as &lt;em&gt;inner&lt;/em&gt; or &lt;em&gt;nested queries&lt;/em&gt;) are queries enclosed within another query. These are useful in cases where you're trying to filter the results of a query against the result of a separate aggregate function.&lt;/p&gt;

&lt;p&gt;To illustrate this idea, say you want to know which of your friends have won more matches than Barbara. Rather than querying how many matches Barbara has won then running another query to see who has won more games than that, you can calculate both with a single query:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="birthdays=#"&gt;SELECT name, wins FROM tourneys 
&lt;/li&gt;&lt;li class="line" prefix="birthdays=#"&gt;WHERE wins &amp;gt; (
&lt;/li&gt;&lt;li class="line" prefix="birthdays=#"&gt;SELECT wins FROM tourneys WHERE name = 'Barbara'
&lt;/li&gt;&lt;li class="line" prefix="birthdays=#"&gt;);
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;  name  | wins 
--------+------
 Dolly  |    7
 Etta   |    4
 Irma   |    9
 Gladys |   13
(4 rows)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The subquery in this statement was run only once; it only needed to find the value from the &lt;code&gt;wins&lt;/code&gt; column in the same row as &lt;code&gt;Barbara&lt;/code&gt; in the &lt;code&gt;name&lt;/code&gt; column, and the data returned by the subquery and outer query are independent of one another. There are cases, though, where the outer query must first read every row in a table and compare those values against the data returned by the subquery in order to return the desired data. In this case, the subquery is referred to as a &lt;em&gt;correlated subquery&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The following statement is an example of a correlated subquery. This query seeks to find which of your friends have won more games than is the average for those with the same shoe size:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="birthdays=#"&gt;SELECT name, size FROM tourneys AS t 
&lt;/li&gt;&lt;li class="line" prefix="birthdays=#"&gt;WHERE wins &amp;gt; (
&lt;/li&gt;&lt;li class="line" prefix="birthdays=#"&gt;SELECT AVG(wins) FROM tourneys WHERE size = t.size
&lt;/li&gt;&lt;li class="line" prefix="birthdays=#"&gt;);
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In order for the query to complete, it must first collect the &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;size&lt;/code&gt; columns from the outer query. Then, it compares each row from that result set against the results of the inner query, which determines the average number of wins for individuals with identical shoe sizes. Because you only have two friends that have the same shoe size, there can only be one row in the result-set:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt; name | size 
------+------
 Etta |    9
(1 row)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As mentioned earlier, subqueries can be used to query results from multiple tables. To illustrate this with one final example, say you wanted to throw a surprise dinner for the group's all-time best bowler. You could find which of your friends has the best bowling record and return their favorite meal with the following query:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="birthdays=#"&gt;SELECT name, entree, side, dessert 
&lt;/li&gt;&lt;li class="line" prefix="birthdays=#"&gt;FROM dinners 
&lt;/li&gt;&lt;li class="line" prefix="birthdays=#"&gt;WHERE name = (SELECT name FROM tourneys 
&lt;/li&gt;&lt;li class="line" prefix="birthdays=#"&gt;WHERE wins = (SELECT MAX(wins) FROM tourneys));
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;  name  | entree | side  |  dessert  
--------+--------+-------+-----------
 Gladys | steak  | fries | ice cream
(1 row)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notice that this statement not only includes a subquery, but also contains a subquery within that subquery. &lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Issuing queries is one of the most commonly-performed tasks within the realm of database management. There are a number of database administration tools, such as &lt;a href="https://www.phpmyadmin.net/"&gt;phpMyAdmin&lt;/a&gt; or &lt;a href="https://www.pgadmin.org/"&gt;pgAdmin&lt;/a&gt;, that allow you to perform queries and visualize the results, but issuing &lt;code&gt;SELECT&lt;/code&gt; statements from the command line is still a widely-practiced workflow that can also provide you with greater control.&lt;/p&gt;

&lt;p&gt;If you're new to working with SQL, we encourage you to use our &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-manage-sql-database-cheat-sheet"&gt;SQL Cheat Sheet&lt;/a&gt; as a reference and to review the &lt;a href="https://www.postgresql.org/docs/10/static/index.html"&gt;official PostgreSQL documenation&lt;/a&gt;. Additionally, if you'd like to learn more about SQL and relational databases, the following tutorials may be of interest to you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.digitalocean.com/community/tutorials/understanding-sql-and-nosql-databases-and-different-database-models"&gt;Understanding SQL And NoSQL Databases And Different Database Models&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.digitalocean.com/community/tutorials/how-to-set-up-logical-replication-with-postgresql-10-on-ubuntu-18-04"&gt;How To Set Up Logical Replication with PostgreSQL 10 on Ubuntu 18.04&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.digitalocean.com/community/tutorials/how-to-secure-postgresql-against-automated-attacks"&gt;How To Secure PostgreSQL Against Automated Attacks&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/usando-uma-cdn-para-acelerar-a-entrega-de-conteudo-estatico-pt</id>
    <published>2018-10-17T00:38:31Z</published>
    <updated>2018-10-17T00:45:28Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/usando-uma-cdn-para-acelerar-a-entrega-de-conteudo-estatico-pt"/>
    <title>Usando uma CDN para Acelerar a Entrega de Conteúdo Estático</title>
    <content type="html">&lt;h3 id="introdução"&gt;Introdução&lt;/h3&gt;

&lt;p&gt;Websites e aplicações modernas geralmente entregam uma quantidade significativa de conteúdo estático para os usuários finais. Este conteúdo inclui imagens, folhas de estilo, JavaScript, e vídeo. À medida que esses recursos estáticos aumentam em número e tamanho, o uso da largura de banda aumenta e o tempo de carregamento da página aumenta, deteriorando a experiência de navegação dos usuários e reduzindo a capacidade disponível dos servidores.&lt;/p&gt;

&lt;p&gt;Para reduzir drasticamente o tempo de carregamento de página, aumentar a performance e reduzir seus custos com largura de banda e infraestrutura, você pode implementar uma CDN, ou rede de entrega de conteúdo para armazenar em cache esses recursos em um conjunto de servidores distribuídos geograficamente.&lt;/p&gt;

&lt;p&gt;Neste tutorial, vamos fornecer uma visão geral de alto nível de CDNs e como elas funcionam, bem como os benefícios que elas podem trazer para suas aplicações web. &lt;/p&gt;

&lt;h2 id="o-que-é-uma-cdn"&gt;O que é uma CDN?&lt;/h2&gt;

&lt;p&gt;Content delivery network ou rede de entrega de conteúdo é um grupo de servidores geograficamente distribuídos otimizados para entregar conteúdo estático aos usuários finais. Esse conteúdo estático pode ser praticamente qualquer tipo de dados, mas as CDNs são mais comumente usadas para entregar páginas web e seus arquivos relacionados, streaming de vídeo e áudio e grandes pacotes de software.&lt;/p&gt;

&lt;p&gt;&lt;img src="http://assets.digitalocean.com/articles/CDN/without-CDN.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Uma CDN consiste em múltiplos &lt;em&gt;pontos de presença&lt;/em&gt; (PoPs) em várias localidades, cada qual consistindo de vários servidores de &lt;em&gt;borda&lt;/em&gt; que armazenam em cache recursos de sua &lt;em&gt;origem&lt;/em&gt;, ou servidor de hospedagem. Quando um usuário visita seu website e solicita recursos estáticos como iamgens ou arquivos de JavaScript, suas solicitações são encaminhadas pela CDN para o servidor de borda mais próximo, a partir do qual o conteúdo é servido. Se o servidor de borda não tem os recursos em cache ou o cache de recursos expirou, a CDN irá buscar e armazenar em cache a versão mais recente de outro servidor de borda da CDN mais próximo ou de seus servidores de origem. Se a borda da CDN tiver uma entrada de cache para seus recursos (o que ocorre na maior parte do tempo se seu site receber uma quantidade moderada de tráfego), ela retornará a cópia em cache para o usuário final.&lt;/p&gt;

&lt;p&gt;&lt;img src="http://assets.digitalocean.com/articles/CDN/CDN.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Isso permite que usuários geograficamente dispersos minimizem o número de saltos necessários para receber o conteúdo estático, buscando o conteúdo diretamente do cache de uma borda próxima. O resultado é latência e perda de pacotes significativamente reduzidos, tempos de carregamento de página mais rápidos e uma carga drasticamente reduzida na sua infraestrutura de origem. &lt;/p&gt;

&lt;p&gt;Os provedores de CDN oferecem recursos adicionais como mitigação de &lt;a href="https://www.digitalocean.com/community/tutorials/digitalocean-community-glossary#ddos-attack"&gt;DDoS&lt;/a&gt; e limitação de taxa, análise de usuários e otimizações para casos de uso móvel ou de streaming com custo adicional.&lt;/p&gt;

&lt;h2 id="como-uma-cdn-funciona"&gt;Como uma CDN funciona?&lt;/h2&gt;

&lt;p&gt;Quando um usuário visita seu website, ele primeiro recebe uma resposta de um servidor de DNS contendo o endereço IP do host do seu servidor web. Seu navegador então solicita o conteúdo da página web, que geralmente consite de uma variedade de arquivos estáticos, como páginas HTML, folhas de estilo CSS, código JavaScript e imagens.&lt;/p&gt;

&lt;p&gt;Uma vez lançada a CDN e descarregados esses recursos estáticos em sevidores da CDN, "empurrando-os" manualmente ou fazendo com que a CDN "puxe" os recursos automaticamente (ambos os mecanismos são cobertos na &lt;a href="https://www.digitalocean.com/community/tutorials/using-a-cdn-to-speed-up-static-content-delivery#push-vs-pull-zones"&gt;próxima seção&lt;/a&gt;), você então instrui seu webserver a reescrever os links para conteúdo estático, de modo que esses links agora apontem para arquivos hospedados pela CDN. Se você estiver usando um CMS como o WordPress, essa reescrita de link pode ser implementada usando um plugin de terceiros como o &lt;a href="https://wordpress.org/plugins/cdn-enabler/"&gt;CDN Enabler&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Muitas CDNs fornecem suporte para domínios personalizados, permitindo que você crie um registro CNAME em seu domínio apontando para um endpoint da CDN. Depois que a CDN recebe uma solicitação do usuário nesse endpoint (localizado na borda, muito mais perto do usuário do que seus servidores de back-end), ela então encaminha a solicitação para o Ponto de Presença (PoP) localizado mais próximo do usuário. Este PoP geralmente consiste de um ou mais servidores de borda da CDN colocados em um ponto de troca de Internet (IxP), essencialmente um datacenter que os Provedores de Serviço de Internet (ISPs) utilizam para interconectar suas redes. O balanceador de carga interno da CDN então encaminha a solicitação para um servidor de borda localizado neste PoP, que então serve o conteúdo para o usuário.  &lt;/p&gt;

&lt;p&gt;Os mecanismos de cache variam entre os provedores de CDN, mas geralmente funcionam da seguinte maneira:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Quando a CDN recebe uma primeira solicitação para um recurso estático, como uma imagem PNG, ele não tem o recurso em cache e deve buscar uma cópia do recurso de um servidor de borda de CDN próximo ou do próprio servidor de origem. Isso é conhecido como cache "miss" e geralmente pode ser detectado inspecionando o cabeçalho de resposta HTTP, contendo &lt;code&gt;X-Cache: MISS&lt;/code&gt;. Essa solicitação inicial será mais lenta que as solicitações futuras porque, depois de concluir essa solicitação, o recurso terá sido armazenado em cache na borda.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;As solicitações futuras para esse recurso ("hits" do cache), encaminhadas para esse local de borda, agora serão atendidas a partir do cache, até a expiração (normalmente definida através de cabeçalhos HTTP). Essas respostas serão significativamente mais rápidas do que a solicitação inicial, reduzindo drasticamente as latências para os usuários e transferindo o tráfego web para a rede da CDN. Você pode verificar se a resposta foi atendida a partir de um cache CDN, inspecionando o cabeçalho de resposta HTTP, que agora deve conter &lt;code&gt;X-Cache: HIT&lt;/code&gt;. &lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Para saber mais sobre como uma CDN específica funciona e como foi implementada, consulte a documentação do seu provedor de CDN.&lt;/p&gt;

&lt;p&gt;Na próxima seção, vamos introduzir os dois tipos populares de CDNs: As CDNs &lt;strong&gt;push&lt;/strong&gt; e &lt;strong&gt;pull&lt;/strong&gt; &lt;/p&gt;

&lt;h2 id="zonas-push-versus-zonas-pull"&gt;Zonas Push versus Zonas Pull&lt;/h2&gt;

&lt;p&gt;A maioria dos provedores de CDN oferecem duas maneiras de armazenar seus dados em cache: zonas pull e zonas push.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Zonas Pull&lt;/strong&gt; envolvem a entrada do endereço do seu servidor de origem, e deixar a CDN buscar e armazenar em cache automaticamente todos os recursos estáticos disponíveis em seu site. Zonas Pull são comumente usadas para fornecer recursos web de pequeno a médio porte frequentemente atualizados, como arquivos HTML, CSS e arquivos JavaScript. Depois de fornecer à CDN o endereço do servidor de origem, a próxima etapa é geralmente reconfigurar links para recursos estáticos, de forma que eles agora apontem para o URL fornecido pela CDN. A partir desse ponto, o CDN manipulará as solicitações de recursos de entrada dos usuários e fornecerá conteúdo de seus caches geograficamente distribuídos e sua origem, conforme apropriado.&lt;/p&gt;

&lt;p&gt;Para utilizar uma &lt;strong&gt;Zona Push&lt;/strong&gt;, você faz o upload de seus dados para um bucket ou local de armazenamento designado, que a CDN então envia para caches em sua frota distribuída de servidores de borda. As zonas Push são normalmente usadas para arquivos maiores, atualizados com pouca frequência, como arquivamentos, pacotes de software, PDFs, vídeo e arquivos de áudio.&lt;/p&gt;

&lt;h2 id="benefícios-do-uso-de-uma-cdn"&gt;Benefícios do Uso de uma CDN&lt;/h2&gt;

&lt;p&gt;Quase qualquer site pode colher os benefícios fornecidos pela implementação de uma CDN, mas geralmente as principais razões para implementá-la são descarregar a largura de banda de seus servidores de origem nos servidores CDN e reduzir a latência para usuários distribuídos geograficamente.&lt;/p&gt;

&lt;p&gt;Vamos passar por estas e várias outras grandes vantagens oferecidas pelo uso de uma CDN logo abaixo.&lt;/p&gt;

&lt;h3 id="descarregamento-da-origem"&gt;Descarregamento da Origem&lt;/h3&gt;

&lt;p&gt;Se você estiver se aproximando da capacidade de largura de banda em seus servidores, o descarregamento de recursos estáticos como imagens, vídeos, arquivos CSS e JavaScript reduzirá drasticamente o uso da largura de banda dos servidores. Redes de entrega de conteúdo são projetadas e otimizadas para servir conteúdo estático, e as solicitações de clientes para esse conteúdo serão encaminhadas e servidas por servidores CDN de borda. Isso traz o benefício adicional de reduzir a carga em seus servidores de origem, pois eles servem esses dados com uma frequência muito menor.&lt;/p&gt;

&lt;h3 id="latência-mais-baixa-para-uma-melhor-experiência-do-usuário"&gt;Latência Mais Baixa para uma Melhor Experiência do Usuário&lt;/h3&gt;

&lt;p&gt;Se sua base de usuários estiver geograficamente dispersa e uma parte não trivial de seu tráfego vier de uma área geográfica distante, uma CDN poderá diminuir a latência ao colocar em cache os recursos estáticos em servidores de borda mais próximos dos seus usuários. Ao reduzir a distância entre os usuários e o conteúdo estático, você pode fornecer conteúdo para seus usuários com mais rapidez e melhorar a experiência aumentando as velocidades de carregamento de páginas.&lt;/p&gt;

&lt;p&gt;Esses benefícios são compostos para websites que atendem principalmente a conteúdo de vídeo com uso intensivo de largura de banda, em que altas latências e lentidão no tempo de carregamento afetam diretamente a experiência do usuário e o engajamento de conteúdo.&lt;/p&gt;

&lt;h3 id="gerenciar-picos-de-tráfego-e-evitar-tempo-de-inatividade"&gt;Gerenciar Picos de Tráfego e Evitar Tempo de Inatividade&lt;/h3&gt;

&lt;p&gt;As CDNs permitem lidar com grandes picos e rajadas de tráfego através do balanceamento da carga de solicitações em uma grande rede distribuída de servidores de borda. Ao descarregar e armazenar em cache o conteúdo estático em uma rede de entrega, você pode acomodar um número maior de usuários simultâneos com sua infraestrutura atual.&lt;/p&gt;

&lt;p&gt;Para sites que usam um único servidor de origem, esses grandes picos de tráfego podem sobrecarregar o sistema, causando interrupções e tempo de inatividade não planejados. A transferência do tráfego para uma infraestrutura de CDN altamente disponível e redundante, projetada para lidar com níveis variáveis de tráfego web, pode aumentar a disponibilidade de seus recursos e do seu conteúdo.&lt;/p&gt;

&lt;h3 id="reduzir-custos"&gt;Reduzir Custos&lt;/h3&gt;

&lt;p&gt;Como a veiculação de conteúdo estático geralmente ocupa a maior parte do uso da largura de banda, o descarregamento desses recursos em uma rede de distribuição de conteúdo pode reduzir drasticamente os gastos mensais com infraestrutura. Além de reduzir os custos de largura de banda, uma CDN pode reduzir os custos de servidor através da redução da carga nos servidores de origem, permitindo escalar a infraestrutura existente. Por fim, alguns provedores de CDN oferecem faturamento mensal com preço fixo, permitindo que você transforme seu uso de largura de banda mensal variável em um gasto recorrente estável e previsível.&lt;/p&gt;

&lt;h3 id="aumentar-a-segurança"&gt;Aumentar a Segurança&lt;/h3&gt;

&lt;p&gt;Outro caso de uso comum para CDNs é a mitigação de ataques de DDoS. Muitos provedores de CDN incluem recursos para monitorar e filtrar solicitações para servidores de borda. Esses serviços analisam o tráfego web em busca de padrões suspeitos, bloqueando o tráfego de ataque mal-intencionado e, ao mesmo tempo, permitindo o tráfego de usuários confiáveis. Os provedores de CDN geralmente oferecem uma variedade de serviços de mitigação de DDoS, desde proteção contra ataques comuns no nível de infra-estrutura (&lt;a href="https://en.wikipedia.org/wiki/Denial-of-service_attack#Types"&gt;camadas OSI 3 e 4&lt;/a&gt;), até serviços de mitigação mais avançados e limitação de taxa.&lt;/p&gt;

&lt;p&gt;Além disso, a maioria das CDNs permite configurar o SSL completo, para que você possa criptografar o tráfego entre a CDN e o usuário final, bem como o tráfego entre a CDN e seus servidores de origem, usando certificados SSL personalizados ou fornecidos pela própria CDN. &lt;/p&gt;

&lt;h3 id="escolhendo-a-melhor-solução"&gt;Escolhendo a Melhor Solução&lt;/h3&gt;

&lt;p&gt;Se o seu gargalo for a carga da CPU no servidor de origem e não a largura de banda, uma CDN pode não ser a solução mais apropriada. Nesse caso, o cache local usando caches populares, como NGINX ou Varnish, pode reduzir significativamente a carga servindo os recursos a partir da memória do sistema.&lt;/p&gt;

&lt;p&gt;Antes de lançar uma CDN, etapas adicionais de otimização — como minimizar e compactar arquivos JavaScript e CSS e ativar a compactação de solicitação HTTP do servidor Web — podem também ter um impacto significativo em tempos de carregamento de páginas e uso de largura de banda. &lt;/p&gt;

&lt;p&gt;Uma ferramenta útil para avaliar a velocidade de carregamento de página e melhorá-la é o &lt;a href="https://developers.google.com/speed/pagespeed/insights/"&gt;PageSpeed Insights&lt;/a&gt; do Google. Outra ferramenta útil que fornece um detalhamento em cascata de solicitações e tempos de resposta, bem como otimizações sugeridas é o &lt;a href="https://www.pingdom.com/"&gt;Pingdom&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id="conclusão"&gt;Conclusão&lt;/h2&gt;

&lt;p&gt;Uma rede de entrega de conteúdo pode ser uma solução rápida e eficaz para melhorar a escalabilidade e disponibilidade de seus websites. Ao armazenar em cache os recursos estáticos em uma rede geograficamente distribuída de servidores otimizados, você pode reduzir muito os tempos de carregamento de página e as latências para os usuários finais. Além disso, as CDNs permitem que você reduza significativamente o uso de largura de banda, absorvendo as solicitações dos usuários e respondendo a partir do cache na borda, reduzindo assim seus custos de largura de banda e infra-estrutura. &lt;/p&gt;

&lt;p&gt;Com plugins e suporte de terceiros para os principais frameworks como WordPress, Drupal, Django e Ruby on Rails, além de recursos adicionais como mitigação de DDoS, SSL completo, monitoramento de usuários e compactação de recursos, as CDNs podem ser uma ferramenta de impacto para proteger e otimizar websites de alto tráfego.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Por Hanif Jetha&lt;/em&gt;&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/como-criar-um-space-e-uma-api-key-na-digitalocean-pt</id>
    <published>2018-10-11T20:59:03Z</published>
    <updated>2018-10-11T21:01:44Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/como-criar-um-space-e-uma-api-key-na-digitalocean-pt"/>
    <title>Como criar um Space e uma API Key na DigitalOcean</title>
    <content type="html">&lt;h3 id="introdução"&gt;Introdução&lt;/h3&gt;

&lt;p&gt;DigitalOcean Spaces é um serviço de armazenamento de objetos que torna mais fácil e econômico armazenar e fornecer grandes quantidades de dados. Spaces individuais podem ser criados e colocados em uso rapidamete, sem necessidade de configuração adicional.&lt;/p&gt;

&lt;p&gt;Neste tutorial, iremos utilizar o Painel de Controle da DigitalOcean para criar um novo Space. Em seguida, iremos recuperar uma Chave de API ou API Key e um secret que podem ser utilizados para conceder acesso ao Space para quaisquer clientes ou bibliotecas compatíveis com S3. &lt;/p&gt;

&lt;h2 id="pré-requisitos"&gt;Pré-requisitos&lt;/h2&gt;

&lt;p&gt;Para completar este tutorial, você vai precisar de uma conta na DigitalOcean. Se você já não tiver uma, você pode registrar uma nova na &lt;a href="https://cloud.digitalocean.com/registrations/new"&gt;página de inscrição&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Faça o login no Painel de Controle da DigitalOcean para começar.&lt;/p&gt;

&lt;h2 id="criando-um-space"&gt;Criando um Space&lt;/h2&gt;

&lt;p&gt;Para criar um novo Space, utilize o botão &lt;strong&gt;Create&lt;/strong&gt; no canto superior direito do Painel de Controle. Clique no botão, em seguida escolha &lt;strong&gt;Spaces&lt;/strong&gt; na lista suspensa:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/space-key/space-menu.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Se você nunca criou um Space antes, você também pode criar um diretamente da página do &lt;strong&gt;Spaces&lt;/strong&gt;. Para fazer isso, clique &lt;strong&gt;Spaces&lt;/strong&gt; na navegação principal do Painel de Controle, e então clique em &lt;strong&gt;Create a space&lt;/strong&gt;. Qualquer uma das opções o levarão à tela &lt;strong&gt;Create a Space&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/space-key/space-creator.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Primeiro, escolha um nome para o seu Space. Esse nome deve ser único entre todos os Spaces (ou seja, nenhum outro usuário do Spaces pode ter o mesmo nome em qualquer região), deve ter de 3 a 63 caracteres, e pode conter apenas letras minúsculas, números e traços.&lt;/p&gt;

&lt;p&gt;Em seguida, escolha a região do datacenter onde você gostaria que seu Space estivesse. No momento em que esta captura de tela foi feita, &lt;code&gt;nyc3&lt;/code&gt; e &lt;code&gt;ams3&lt;/code&gt; eram as escolhas possíveis. Outras se tornarão dsponíveis ao longo do tempo.&lt;/p&gt;

&lt;p&gt;Finalmente, escolha se deseja que os usuários não autenticados possam listar todos os arquivos em seu Space. Isso não afeta o acesso a arquivos individuais (que é definido em uma base por aquivo), mas apenas a capacidade de obter uma lista de todos os arquivos. A escolha padrão de &lt;strong&gt;Private&lt;/strong&gt; é segura, a menos que você tenha alguns scripts ou clientes que precisem buscar listagens de arquivos sem uma chave de acesso ou access key.&lt;/p&gt;

&lt;p&gt;Quando seu nome e as opções estiverem todos definidos, desça e clique no botão &lt;strong&gt;Create a Space&lt;/strong&gt;. Seu Space será criado, e você será levado para a interface do navegador de arquivos: &lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/space-key/space-default.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Se este é o seu primeiro Space, você terá um arquivo &lt;strong&gt;welcome.html&lt;/strong&gt;, do contrário, o Space estará vazio.&lt;/p&gt;

&lt;p&gt;Tome nota da URL do seu Space. Está disponível logo abaixo do nome do Space na visualização do navegador de arquivos. Nesse caso de exemplo, a URL completa é &lt;strong&gt;https://&lt;span class="highlight"&gt;example-name&lt;/span&gt;.nyc3.digitaloceanspaces.com&lt;/strong&gt;. O nome do Space aqui (geralmente chamado de nome do bucket) é &lt;strong&gt;&lt;span class="highlight"&gt;example-name&lt;/span&gt;&lt;/strong&gt;. A URL do servidor (ou endereço) é a parte restante, consistindo do nome do datacenter seguido por &lt;strong&gt;.digitaloceanspaces.com&lt;/strong&gt;: &lt;strong&gt;&lt;a href="https://nyc3.digitaloceanspaces.com/"&gt;https://nyc3.digitaloceanspaces.com&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Existem algumas maneiras diferentes pelas quais os clientes e bibliotecas solicitarão essas informações. Alguns vão querer no mesmo formato dado no Painel de Controle. Alguns exigem que o nome do bucket siga a URL do servidor, como em &lt;a href="https://nyc3.digitaloceanspaces.com/"&gt;https://nyc3.digitaloceanspaces.com/&lt;/a&gt;&lt;span class="highlight"&gt;example-name&lt;/span&gt;. Outros ainda pedirão para você inserir o endereço do servidor e o nome do bucket ou Space separadamente. Consulte a documentação do seu cliente ou biblioteca para mais orientações nesse item.&lt;/p&gt;

&lt;p&gt;A seguir, criaremos a chave que precisamos para acessar nossos Spaces a partir de clientes de terceiros.&lt;/p&gt;

&lt;h2 id="criando-uma-access-key"&gt;Criando uma Access Key&lt;/h2&gt;

&lt;p&gt;Para acessar seus arquivos de fora do Painel de Controle da DigitalOcean, precisamos gerar uma chave de acesso ou &lt;strong&gt;access key&lt;/strong&gt; e um &lt;strong&gt;secret&lt;/strong&gt;. Estes são um par de tokens aleatórios que servem como nome de usuário e senha para conceder acesso ao seu Space. &lt;/p&gt;

&lt;p&gt;Primeiro, clique no link da &lt;strong&gt;API&lt;/strong&gt; na navegação principal do Painel de Controle. A página resultante lista seus tokens de &lt;strong&gt;API da DigitalOcean&lt;/strong&gt; e as chaves de acesso do &lt;strong&gt;Spaces&lt;/strong&gt;. Role para baixo até a parte do Spaces: &lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/space-key/keys-default.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Se este é o seu primeiro Space, você não pode ter nenhuma chave listada. Clique no botão &lt;strong&gt;Generate New Key&lt;/strong&gt;. A caixa de diálogo &lt;strong&gt;New Spaces key&lt;/strong&gt; será exibida:   &lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/space-key/new-key-dialog.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Digite um nome para a chave. Você pode criar quantas chaves quiser, portanto, lembre-se de que a única maneira de revogar o acesso a uma chave é excluí-la. Desse modo, você pode querer particionar as chaves por pessoa, por equipe ou pelo software cliente no qual você as estiver utilizando.&lt;/p&gt;

&lt;p&gt;Neste caso, estamos criando uma chave chamada &lt;span class="highlight"&gt;example-token&lt;/span&gt;. Clique no botão &lt;strong&gt;Generate Key&lt;/strong&gt; para completar o processo. Você retornará à tela da API listando todas as suas chaves. Observe que a nova chave tem dois tokens longos exibidos: &lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/space-key/new-key-display.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;O primeiro é a sua &lt;strong&gt;access key&lt;/strong&gt;. Isso não é secreto e continuará visível no Painel de Controle. A segunda string é o seu &lt;strong&gt;secret&lt;/strong&gt; ou &lt;strong&gt;secret key&lt;/strong&gt;. Isso só será exibido uma vez. Registre-a em um local seguro para uso posterior. Na próxima vez que você visitar a página da API, esse valor será eliminado e não há como recuperá-lo.  &lt;/p&gt;

&lt;p&gt;Diferentes clientes compatíveis com S3 podem ter nomes sutilmente diferentes para &lt;strong&gt;access key&lt;/strong&gt; e &lt;strong&gt;secret&lt;/strong&gt;. A terminologia usada é normalmente próxima o suficiente para deixar claro qual token deve ir para onde. Caso contrário, consulte a documentação do seu cliente ou biblioteca para obter mais informações.&lt;/p&gt;

&lt;h2 id="conclusão"&gt;Conclusão&lt;/h2&gt;

&lt;p&gt;Neste tutorial criamos um novo Space na DigitalOcean e uma nova access key e secret. Agora sabemos nossa &lt;strong&gt;URL de servidor&lt;/strong&gt;, &lt;strong&gt;nome do bucket&lt;/strong&gt; (ou nome do Space), &lt;strong&gt;access key&lt;/strong&gt;, e &lt;strong&gt;secret&lt;/strong&gt;. Com essas informações você pode conectar praticamente qualquer cliente ou biblioteca compatível com S3 ao seu novo Space na DigitalOcean!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Por Brian Boucheron&lt;/em&gt;&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/como-configurar-pipelines-de-integracao-continua-com-o-gitlab-ci-no-ubuntu-16-04-pt</id>
    <published>2018-10-11T03:28:49Z</published>
    <updated>2018-10-11T03:31:04Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/como-configurar-pipelines-de-integracao-continua-com-o-gitlab-ci-no-ubuntu-16-04-pt"/>
    <title>Como Configurar Pipelines de Integração Contínua com o GitLab CI no Ubuntu 16.04</title>
    <content type="html">&lt;h3 id="introdução"&gt;Introdução&lt;/h3&gt;

&lt;p&gt;O GitLab Community Edition é um provedor de repositório Git auto-hospedado com recursos adicionais para ajudar no gerenciamento de projetos e no desenvolvimento de software. Um dos recursos mais valiosos que o GitLab oferece é a ferramenta embutida de integração e entrega contínua chamada &lt;a href="https://about.gitlab.com/features/gitlab-ci-cd/"&gt;GitLab CI&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Neste guia, vamos demonstrar como configurar o GitLab CI para monitorar seus repositórios por mudanças e executar testes automatizados para validar código novo. Começaremos com uma instalação do GitLab em execução, na qual copiaremos um repositório de exemplo para uma aplicação básica em Node.js. Depois de configurarmos nosso processo de CI, quando um novo commit é enviado ao repositório o GitLab irá utilizar o CI runner para executar o conjunto de testes em cima do código em um container Docker isolado. &lt;/p&gt;

&lt;h2 id="pré-requisitos"&gt;Pré-requisitos&lt;/h2&gt;

&lt;p&gt;Antes de começarmos, você precisará configurar um ambiente inicial. Vamos precisar de um servidor GitLab seguro configurado para armazenar nosso código e gerenciar nosso processo de CI/CD. Adicionalmente, precisaremos de um local para executar os testes automatizados. Este pode ser o mesmo servidor em que o GitLab está instalado ou um host separado. As seções abaixo cobrem os requisitos em mais detalhes. &lt;/p&gt;

&lt;h3 id="um-servidor-gitlab-protegido-com-ssl"&gt;Um Servidor GitLab Protegido com SSL&lt;/h3&gt;

&lt;p&gt;Para armazenar nosso código-fonte e configurar nossas tarefas de CI/CD, precisamos de uma instância do GitLab instalada em um servidor Ubuntu 16.04. Atualmente o GitLab recomenda um servidor com no mínimo &lt;strong&gt;2 núcleos de CPU&lt;/strong&gt; e &lt;strong&gt;4GB de RAM&lt;/strong&gt;. Para proteger seu código de ser exposto ou adulterado, a instância do GitLab será protegida com SSL usando o Let's Encrypt. Seu servidor precisa ter um nome de domínio associado a ele para completar essa etapa.&lt;/p&gt;

&lt;p&gt;Você pode atender esses requisitos usando os seguintes tutoriais:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.digitalocean.com/community/tutorials/configuracao-inicial-de-servidor-com-ubuntu-16-04-pt"&gt;Configuração Inicial de servidor com Ubuntu 16.04&lt;/a&gt;: Crie um usuário com privilégios &lt;code&gt;sudo&lt;/code&gt; e configure um firewall básico.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-and-configure-gitlab-on-ubuntu-16-04"&gt;Como Instalar e Configurar o GitLab no Ubuntu 16.04&lt;/a&gt;: Instale o GitLab no servidor e proteja-o com um certificado Let's Encrypt TLS/SSL.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Estaremos demonstrando como compartilhar CI/CD runners (os componentes que executam os testes automatizados). Se você deseja compartilhar CI runners entre projetos, recomendamos fortemente que você restrinja ou desative as inscrições públicas. Se você não modificou suas configurações durante a instalação, volte e siga &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-and-configure-gitlab-on-ubuntu-16-04#restrict-or-disable-public-sign-ups-(optional)"&gt;a etapa opcional do artigo de instalação do GitLab sobre como restringir ou desabilitar as inscrições&lt;/a&gt; para evitar abusos por parte de terceiros. &lt;/p&gt;

&lt;h3 id="um-ou-mais-servidores-para-utilizar-como-gitlab-ci-runners"&gt;Um ou Mais Servidores para Utilizar como GitLab CI Runners&lt;/h3&gt;

&lt;p&gt;GitLab CI Runners são os servidores que verificam o código e executam testes automatizados para validar novas alterações. Para isolar o ambiente de testes, estaremos executando todos os nossos testes automatizados em  containers Docker. Para fazer isso, precisamos instalar o Docker no servidor ou servidores que irão executar os testes.&lt;/p&gt;

&lt;p&gt;Esta etapa pode ser concluída no servidor GitLab ou em outro servidor Ubuntu 16.04 para fornecer isolamento adicional e evitar contenção de recursos. Os seguintes tutoriais instalarão o Docker no host que você deseja usar para executar seus testes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.digitalocean.com/community/tutorials/configuracao-inicial-de-servidor-com-ubuntu-16-04-pt"&gt;Configuração Inicial de servidor com Ubuntu 16.04&lt;/a&gt;: Crie um usuário com privilégios &lt;code&gt;sudo&lt;/code&gt; e configure um firewall básico. (você não precisa completar isso novamente se estiver configurando o CI runner no servidor do GitLab).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.digitalocean.com/community/tutorials/como-instalar-e-usar-o-docker-no-ubuntu-16-04-pt"&gt;Como Instalar e Usar o Docker no Ubuntu 16.04&lt;/a&gt;: Siga os &lt;strong&gt;passos 1 e 2&lt;/strong&gt; para instalar o Docker no servidor.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Quando estiver pronto para começar, continue com este guia.&lt;/p&gt;

&lt;h2 id="copiando-o-repositório-de-exemplo-a-partir-do-github"&gt;Copiando o Repositório de Exemplo a partir do GitHub&lt;/h2&gt;

&lt;p&gt;Para começar, vamos criar um novo projeto no GitLab contendo a aplicação de exemplo em Node.js. Iremos &lt;a href="https://github.com/do-community/hello_hapi/"&gt;importar o repositório original diretamente do GitHub&lt;/a&gt; para que não tenhamos que carregá-lo manualmente.&lt;/p&gt;

&lt;p&gt;Efetue o login no GitLab e clique no ícone de adição no canto superior direito e selecione &lt;strong&gt;New project&lt;/strong&gt; para adicionar um novo projeto: &lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/gitlab_ci_usage/new_project_icon_3.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Na página do novo projeto, clique na aba &lt;strong&gt;Import project&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/gitlab_ci_usage/import-project.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;A seguir, clique no botão &lt;strong&gt;Repo by URL&lt;/strong&gt;. Embora exista uma opção de importação do GitHub, ela requer um token de acesso Pessoal e é usada para importar o repositório e informações adicionais. Estamos interessados apenas no código e no histórico do Git, portanto, importar pela URL é mais fácil.&lt;/p&gt;

&lt;p&gt;No campo &lt;strong&gt;Git repository URL&lt;/strong&gt;, insira a seguinte URL do repositório GitHub:  &lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;https://github.com/do-community/hello_hapi.git
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Deve se parecer com isto:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/gitlab_ci_usage/new_project_github_url2.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Como esta é uma demonstração, provavelmente é melhor manter o repositório marcado como &lt;strong&gt;Private&lt;/strong&gt; ou privado. Quando terminar, clique em &lt;strong&gt;Create project&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;O novo projeto será criado baseado no repositório importado do Github.&lt;/p&gt;

&lt;h2 id="entendendo-o-arquivo-gitlab-ci-yml"&gt;Entendendo o arquivo .gitlab-ci.yml&lt;/h2&gt;

&lt;p&gt;O GitLab CI procura por um arquivo chamado &lt;code&gt;.gitlab-ci.yml&lt;/code&gt; dentro de cada repositório para determinar como ele deve testar o código. O repositório que importamos já tem um arquivo &lt;code&gt;.gitlab-ci.yml&lt;/code&gt; configurado para o projeto. Você pode aprender mais sobre o formato lendo a &lt;a href="https://docs.gitlab.com/ce/ci/yaml/README.html"&gt;documentação de referência do .gitlab-ci.yml&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Clique no arquivo &lt;code&gt;.gitlab-ci.yml&lt;/code&gt; na interface do GitLab para o projeto que acabamos de criar. A configuração do CI deve ser algo assim:&lt;/p&gt;
&lt;div class="code-label " title=".gitlab-ci.yml"&gt;.gitlab-ci.yml&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;
image: node:latest

stages:
  - build
  - test

cache:
  paths:
    - node_modules/

install_dependencies:
  stage: build
  script:
    - npm install
  artifacts:
    paths:
      - node_modules/

test_with_lab:
  stage: test
  script: npm test
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;O arquivo utiliza a &lt;a href="https://docs.gitlab.com/ee/ci/yaml/"&gt;sintaxe de configuração YAML no GitLab CI&lt;/a&gt; para definir as ações que devem ser tomadas, a ordem na qual elas devem executar, sob quais condições elas devem ser executadas e os recursos necessários para concluir cada tarefa. Ao escrever seus próprios arquivos de CI do GitLab, você pode checar com um validador indo até &lt;code&gt;/ci/lint&lt;/code&gt; em sua instância GitLab para validar que seu arquivo está formatado corretamente. &lt;/p&gt;

&lt;p&gt;O arquivo de configuração começa declarando uma &lt;code&gt;image&lt;/code&gt; ou imagem do Docker que deve ser usada para executar o conjunto de testes. Como o Hapi é um framework Node.js, estamos usando a imagem Node.js mais recente:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;image: node:latest
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Em seguida, definimos explicitamente os diferentes estágios de integração contínua que serão executados:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;stages:
  - build
  - test
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Os nomes que você escolhe aqui são arbitrários, mas a ordenação determina a ordem de execução dos passos que se seguirão. Stages ou estágios são tags que você pode aplicar a jobs individuais. O GitLab vai executar jobs do mesmo estágio em paralelo e vai esperar para executar o próximo estágio até que todos os jobs do estágio atual estejam completos. Se nenhum estágio for definido, o GitLab usará três estágios chamados &lt;code&gt;build&lt;/code&gt;, &lt;code&gt;test&lt;/code&gt;, e &lt;code&gt;deploy&lt;/code&gt; e atribuir todos os jobs ao estágio &lt;code&gt;test&lt;/code&gt; por padrão. &lt;/p&gt;

&lt;p&gt;Após definir os estágios, a configuração inclui uma definição de &lt;code&gt;cache&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;cache:
  paths:
    - node_modules/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Isso especifica arquivos ou diretórios que podem ser armazenados em cache (salvos para uso posterior) entre execuções ou estágios. Isso pode ajudar a diminuir o tempo necessário para executar tarefas que dependem de recursos que podem não ser alterados entre execuções. Aqui, estamos fazendo cache do diretório &lt;code&gt;node_modules&lt;/code&gt;, que é onde o &lt;code&gt;npm&lt;/code&gt; instala as dependências que ele baixa.&lt;/p&gt;

&lt;p&gt;Nosso primeiro job é chamado &lt;code&gt;install_dependencies&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;install_dependencies:
  stage: build
  script:
    - npm install
  artifacts:
    paths:
      - node_modules/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Os jobs podem ter qualquer nome, mas como os nomes serão usados na interface do usuário do GitLab, nomes descritivos são úteis. Normalmente, o &lt;code&gt;npm install&lt;/code&gt; pode ser combinado com os próximos estágios de teste, mas para melhor demonstrar a interação entre os estágios, estamos extraindo essa etapa para executar em seu próprio estágio.&lt;/p&gt;

&lt;p&gt;Marcamos o estágio explicitamente como "build" com a diretiva &lt;code&gt;stage&lt;/code&gt;. Em seguida, especificamos os comandos reais a serem executados usando a diretiva &lt;code&gt;script&lt;/code&gt;. Você pode incluir vários comandos inserindo linhas adicionais dentro da seção &lt;code&gt;script&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A sub-seção &lt;code&gt;artifacts&lt;/code&gt; é utilizada para especificar caminhos de arquivo ou diretório para salvar e passar entre os estágios. Como o comando &lt;code&gt;npm install&lt;/code&gt; instala as dependências do projeto, nossa próxima etapa precisará de acesso aos arquivos baixados. A declaração do caminho &lt;code&gt;node_modules&lt;/code&gt; garante que o próximo estágio terá acesso aos arquivos. Estes estarão também disponíveis para visualizar ou baixar na interface de usuário do GitLab após o teste, assim isso é útil para construir artefatos como binários também. Se você quiser salvar tudo que foi produzido durante o estágio, substitua a seção &lt;code&gt;path&lt;/code&gt; inteira por &lt;code&gt;untracked: true&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Finalmente, o segundo job chamado &lt;code&gt;test_with_lab&lt;/code&gt; declara o comando que realmente executará o conjunto de testes:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;test_with_lab:
  stage: test
  script: npm test
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Colocamos isso no estágio &lt;code&gt;test&lt;/code&gt;. Como esse é o último estágio, ele tem acesso aos artefatos produzidos pelo estágio &lt;code&gt;build&lt;/code&gt; que são as dependências do projeto em nosso caso. Aqui, a seção &lt;code&gt;script&lt;/code&gt; demonstra a sintaxe YAML de linha única que pode ser usada quando há apenas um único item. Poderíamos ter usado essa mesma sintaxe no job anterior, já que apenas um comando foi especificado.&lt;/p&gt;

&lt;p&gt;Agora que você tem uma ideia básica sobre como o arquivo &lt;code&gt;.gitlab-ci.yml&lt;/code&gt; define tarefas CI/CD, podemos definir um ou mais runners capazes de executar o plano de testes.&lt;/p&gt;

&lt;h2 id="disparando-uma-execução-de-integração-contínua"&gt;Disparando uma Execução de Integração Contínua&lt;/h2&gt;

&lt;p&gt;Como o nosso repositório inclui um arquivo &lt;code&gt;.gitlab-ci.yml&lt;/code&gt;, quaisquer novos commits irão disparar uma nova execução de CI. Se não houver runners disponíveis, a execução da CI será definida como "pending" ou pendente. Antes de definirmos um runner, vamos disparar uma execução de CI para ver como é um job no estado pendente. Uma vez que um runner esteja disponível, ele imediatamente pegará a execução pendente.&lt;/p&gt;

&lt;p&gt;De volta à visão do repositório do projeto do GitLab &lt;code&gt;hello_hapi&lt;/code&gt;, clique no &lt;strong&gt;sinal de adição&lt;/strong&gt; ao lado do branch e do nome do projeto e selecione &lt;strong&gt;New file&lt;/strong&gt; no menu:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/gitlab_ci_usage/new_file_button2.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Na próxima página, insira &lt;code&gt;dummy_file&lt;/code&gt; no campo &lt;strong&gt;File name&lt;/strong&gt; e insira algum texto na janela principal de edição:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/gitlab_ci_usage/dummy_file2.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Clique em &lt;strong&gt;commit changes&lt;/strong&gt; na parte inferior quando terminar.&lt;/p&gt;

&lt;p&gt;Agora, retorne à página principal do projeto. Um pequeno ícone de &lt;strong&gt;pausa&lt;/strong&gt; será anexado ao commit mais recente. Se você passar o mouse sobre o ícone, ele irá exibir "Commit:pending":&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/gitlab_ci_usage/pending_marker_2.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Isso significa que os testes que validam as alterações de código ainda não foram executados.&lt;/p&gt;

&lt;p&gt;Para obter mais informações, vá para o topo da página e clique em &lt;strong&gt;Pipelines&lt;/strong&gt;. Você será direcionado para a página de visão geral do pipeline, na qual é possível ver que a execução CI está marcada como pending e rotulada como "stuck":&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/gitlab_ci_usage/pipeline_index_stuck.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;&lt;span class='note'&gt;&lt;strong&gt;Nota:&lt;/strong&gt; Do lado direito há um botão para a ferramenta &lt;strong&gt;CI Lint&lt;/strong&gt;. É aqui que você pode verificar a sintaxe de qualquer arquivo &lt;code&gt;gitlab-ci.yml&lt;/code&gt; que você escreve.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;A partir daqui, você pode clicar no status &lt;strong&gt;pending&lt;/strong&gt; para obter mais detalhes sobre a execução. Esta visão mostra os diferentes estágios de nossa execução, bem como os jobs individuais associados a cada estágio:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/gitlab_ci_usage/pipeline_detail_view.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Finalmente, clique no job &lt;strong&gt;install_dependencies&lt;/strong&gt;. Isso lhe dará detalhes específicos sobre o que está atrasando a execução:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/gitlab_ci_usage/job_detail_view.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Aqui, a mensagem indica que o trabalho está preso devido à falta de runners. Isso é esperado, uma vez que ainda não configuramos nenhum. Quando um runner estiver disponível, essa mesma interface poderá ser usada para ver a saída. Este é também o local onde você pode baixar os artefatos produzidos durante o build.&lt;/p&gt;

&lt;p&gt;Agora que sabemos como é um job pendente, podemos atribuir um runner de CI ao nosso projeto para pegar o job pendente.&lt;/p&gt;

&lt;h2 id="instalando-o-serviço-ci-runner-do-gitlab"&gt;Instalando o Serviço CI Runner do GitLab&lt;/h2&gt;

&lt;p&gt;Agora estamos prontos para configurar um CI Runner do GitLab. Para fazer isso, precisamos instalar o pacote CI runner do GitLab no sistema e iniciar o serviço do runner. O serviço pode executar várias instâncias do runner para projetos diferentes.&lt;/p&gt;

&lt;p&gt;Como mencionado nos pré-requisitos, você pode completar estes passos no mesmo servidor que hospeda sua instância do GitLab ou em um servidor diferente se você quiser ter certeza de evitar a contenção de recursos. Lembre-se de que, seja qual for o host escolhido, você precisa do Docker instalado para a configuração que usaremos.&lt;/p&gt;

&lt;p&gt;O processo de instalação do serviço CI runner do GitLab é similar ao processo usado para instalar o próprio GitLab. Iremos baixar um script para adicionar um repositório GitLab à nossa lista de fontes &lt;code&gt;apt&lt;/code&gt;. Depois de executar o script, faremos o download do pacote do runner. Podemos então configurá-lo para servir nossa instância do GitLab.&lt;/p&gt;

&lt;p&gt;Comece baixando a versão mais recente do script de configuração do repositório do GitLab CI runner para o diretório &lt;code&gt;/tmp&lt;/code&gt; (este é um repositório diferente daquele usado pelo servidor GitLab):&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh -o /tmp/gl-runner.deb.sh
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Sinta-se à vontade para examinar o script baixado para garantir que você está confortável com as ações que ele irá tomar. Você também pode encontrar uma versão hospedada do script &lt;a href="https://packages.gitlab.com/runner/gitlab-ci-multi-runner/install"&gt;aqui&lt;/a&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;less /tmp/gl-runner.deb.sh
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Quando estiver satisfeito com a segurança do script, execute o instalador:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo bash /tmp/gl-runner.deb.sh
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;O script irá configurar seu servidor para usar os repositórios mantidos pelo GitLab. Isso permite gerenciar os pacotes do runner do GitLab com as mesmas ferramentas de gerenciamento de pacotes que você usa para os outros pacotes do sistema. Quando isso estiver concluído, você pode prosseguir com a instalação usando &lt;code&gt;apt-get&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt-get install gitlab-runner
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Isso irá instalar o pacote CI runner do GitLab no sistema e iniciar o serviço GitLab runner.&lt;/p&gt;

&lt;h2 id="configurando-um-gitlab-runner"&gt;Configurando um GitLab Runner&lt;/h2&gt;

&lt;p&gt;Em seguida, precisamos configurar um CI runner do GitLab para que ele possa começar a aceitar trabalho.&lt;/p&gt;

&lt;p&gt;Para fazer isso, precisamos de um token do GitLab runner para que o runner possa se autenticar com o servidor GitLab. O tipo de token que precisamos depende de como queremos usar esse runner.&lt;/p&gt;

&lt;p&gt;Um &lt;strong&gt;runner específico do projeto&lt;/strong&gt; é útil se você tiver requisitos específicos para o runner. Por exemplo, se seu arquivo &lt;code&gt;gitlab-ci.yml&lt;/code&gt; define tarefas de deployment que requeiram credenciais, um runner específico pode ser necessário para autenticar corretamente dentro do ambiente de deployment. Se o seu projeto tiver etapas com recursos intensivos no processo do CI, isso também pode ser uma boa ideia. Um runner específico do projeto não irá aceitar jobs de outros projetos.&lt;/p&gt;

&lt;p&gt;Por outro lado, um &lt;strong&gt;runner compartilhado&lt;/strong&gt; é um runner de propósito geral que pode ser utilizado por vários projetos. Os runners receberão jobs dos projetos de acordo com um algoritmo que contabiliza o número de jobs que estão sendo executados atualmente para cada projeto. Esse tipo de runner é mais flexível. Você precisará fazer login no GitLab com uma conta de administrador para configurar os runners compartilhados.&lt;/p&gt;

&lt;p&gt;Vamos demonstrar como obter os tokens de runner para esses dois tipos de runner abaixo. Escolha o método que melhor lhe convier.&lt;/p&gt;

&lt;h3 id="coletando-informações-para-registrar-um-runner-específico-de-projeto"&gt;Coletando Informações para Registrar um Runner Específico de Projeto&lt;/h3&gt;

&lt;p&gt;Se você quiser que o runner seja vinculado a um projeto específico, comece navegando até a página do projeto na interface do GitLab.&lt;/p&gt;

&lt;p&gt;A partir daqui, clique no item &lt;strong&gt;Settings&lt;/strong&gt; no menu à esquerda. Depois, clique no item &lt;strong&gt;CI/CD&lt;/strong&gt; no submenu: &lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/gitlab_ci_usage/project_settings_item2.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Nesta página, você verá uma seção &lt;strong&gt;Runners settings&lt;/strong&gt;. Clique no botão &lt;strong&gt;Expand&lt;/strong&gt; para ver mais detalhes. Na visão de detalhes, o lado esquerdo explicará como registrar um runner específico do projeto. Copie o token de registro exibido na etapa 4 das instruções:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/gitlab_ci_usage/specific_runner_config_settings2.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Se você quiser desativar quaisquer runners compartilhados ativos para este projeto, você pode fazê-lo clicando no botão &lt;strong&gt;Disable shared Runners&lt;/strong&gt; no lado direito. Isso é opcional. &lt;/p&gt;

&lt;p&gt;Quando estiver pronto, avance para aprender como registrar seu runner usando as informações coletadas nesta página.&lt;/p&gt;

&lt;h3 id="coletando-informações-para-registrar-um-runner-compartilhado"&gt;Coletando Informações para Registrar um Runner Compartilhado&lt;/h3&gt;

&lt;p&gt;Para encontrar as informações necessárias para registrar um runner compartilhado, você precisa estar logado com uma conta administrativa.&lt;/p&gt;

&lt;p&gt;Comece clicando no &lt;strong&gt;ícone de chave inglesa&lt;/strong&gt; na barra de navegação superior para acessar a área administrativa. Na seção &lt;strong&gt;Overview&lt;/strong&gt; do menu à esquerda, clique em &lt;strong&gt;Runners&lt;/strong&gt; para acessar a página de configuração do runner compartilhado.  &lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/gitlab_ci_usage/admin_area_icon2.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Copie o token de registro exibido na parte superior da página:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/gitlab_ci_usage/shared_runner_token2.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Usaremos esse token para registrar um runner do GitLab CI para o projeto.&lt;/p&gt;

&lt;h3 id="registrando-um-runner-do-gitlab-ci-com-o-servidor-gitlab"&gt;Registrando um Runner do GitLab CI com o Servidor GitLab&lt;/h3&gt;

&lt;p&gt;Agora que você tem um token, volte para o servidor em que seu serviço do runner do GitLab CI está instalado.&lt;/p&gt;

&lt;p&gt;Para registrar um novo runner, digite o seguinte comando:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo gitlab-runner register
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Você será solicitado a responder uma série de questões para configurar o runner:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Please enter the gitlab-ci coordinator URL (e.g. &lt;a href="https://gitlab.com/"&gt;https://gitlab.com/&lt;/a&gt;)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Insira o nome de domínio do seu servidor GitLab, usando &lt;code&gt;https://&lt;/code&gt; para especificar SSL. Você pode, opcionalmente, anexar &lt;code&gt;/ci&lt;/code&gt; ao final do seu domínio, mas as versões recentes serão redirecionadas automaticamente. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Please enter the gitlab-ci token for this runner&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Insira o token que você copiou na última seção.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Please enter the gitlab-ci description for this runner&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Insira um nome para esse runner particular. Isso será exibido na lista de runners do serviço, na linha de comando e na interface do GitLab.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Please enter the gitlab-ci tags for this runner (comma separated)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Estas são tags que você pode atribuir ao runner. Os jobs do GitLab podem expressar requisitos em termos dessas tags para garantir que eles sejam executados em um host com as dependências corretas.&lt;/p&gt;

&lt;p&gt;Você pode deixar isso em branco neste caso.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Whether to lock Runner to current project [true/false]&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Atribua o runner ao projeto específico. Ele não poderá ser utilizado por outro projeto.&lt;/p&gt;

&lt;p&gt;Selecione "false" aqui.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Please enter the executor&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Insira o método usado pelo runner para completar jobs.&lt;/p&gt;

&lt;p&gt;Escolha "docker" aqui.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Please enter the default Docker image (e.g. ruby:2.1)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Insira a imagem padrão utilizada para executar jobs quando o arquivo &lt;code&gt;.gitlab-ci.yml&lt;/code&gt; não incluir uma especificação de imagem. É melhor especificar uma imagem geral aqui e definir imagens mais específicas em seu  arquivo &lt;code&gt;.gitlab-ci.yml&lt;/code&gt; como fizemos.&lt;/p&gt;

&lt;p&gt;Vamos inserir "alpine:latest" aqui como um padrão pequeno e seguro.&lt;/p&gt;

&lt;p&gt;Depois de responder às questões, um novo runner será criado, capaz de executar os jobs de CI/CD do seu projeto. &lt;/p&gt;

&lt;p&gt;Você pode ver os runners que o serviço de runner do GitLab CI tem atualmente disponíveis digitando:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo gitlab-runner list
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Listing configured runners                          ConfigFile=/etc/gitlab-runner/config.toml
example-runner                                      Executor=docker Token=e746250e282d197baa83c67eda2c0b URL=https://example.com
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Agora que temos um runner disponível, podemos retornar ao projeto no GitLab.&lt;/p&gt;

&lt;h2 id="visualizando-a-execução-de-ci-cd-no-gitlab"&gt;Visualizando a Execução de CI/CD no GitLab&lt;/h2&gt;

&lt;p&gt;De volta ao seu navegador, retorne ao seu projeto no GitLab. Dependendo de quanto tempo passou desde o registro do seu runner, ele pode estar em execução no momento:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/gitlab_ci_usage/ci_running_icon_2.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Ou ele já pode ter sido concluído:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/gitlab_ci_usage/ci_run_passed_icon_2.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Independentemente do estado, clique no ícone &lt;strong&gt;running&lt;/strong&gt; ou &lt;strong&gt;passed&lt;/strong&gt; (ou &lt;strong&gt;failed&lt;/strong&gt; se você se deparou com um problema) para ver o estado atual da execução da CI. Você pode ter uma visualização semelhante clicando no menu superior &lt;strong&gt;Pipelines&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Você será direcionado para a página de visão geral do pipeline, na qual poderá ver o status da execução do GitLab CI:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/gitlab_ci_usage/pipeline_run_overview.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;No cabeçalho &lt;strong&gt;Stages&lt;/strong&gt;, haverá um círculo indicando o status de cada um dos estágios da execução. Se você clicar no estágio, poderá ver os jobs individuais associados ao estágio:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/gitlab_ci_usage/pipeline_run_stage_view.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Clique no job &lt;strong&gt;install_dependencies&lt;/strong&gt; dentro do estágio &lt;strong&gt;build&lt;/strong&gt;. Isso  o levará para a página de visão geral do job:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/gitlab_ci_usage/pipeline_job_overview.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Agora, em vez de exibir uma mensagem de nenhum runner estar disponível, a saída do job é exibida. Em nosso caso, isso significa que você pode ver os resultados do &lt;code&gt;npm&lt;/code&gt; instalando cada um dos pacotes.&lt;/p&gt;

&lt;p&gt;Ao longo do lado direito, você pode ver alguns outros itens também. Você pode ver outros jobs alterando o estágio e clicando nas execuções abaixo. Você também pode visualizar ou baixar quaisquer artefatos produzidos pela execução.&lt;/p&gt;

&lt;h2 id="conclusão"&gt;Conclusão&lt;/h2&gt;

&lt;p&gt;Neste guia, adicionamos um projeto demonstrativo à instância do Gitlab para mostrar os recursos de integração contínua e de deployment do GitLab CI. Discutimos como definir um pipeline nos arquivos &lt;code&gt;gitlab-ci.yml&lt;/code&gt; para construir e testar suas aplicações e como atribuir jobs aos estágios para definir a relação um com o outro. Em seguida, configuramos um runner do GitLab CI para pegar tarefas de CI para nosso projeto e demonstramos como encontrar informações sobre execuções individuais da CI do GitLab.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Por Justin Ellingwood&lt;/em&gt;&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-use-git-a-reference-guide</id>
    <published>2018-10-04T20:49:46Z</published>
    <updated>2018-10-10T16:55:08Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-use-git-a-reference-guide"/>
    <title>How To Use Git: A Reference Guide</title>
    <content type="html">&lt;h2 id="git-cheat-sheet"&gt;Git Cheat Sheet&lt;/h2&gt;

&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;Teams of developers and open-source software maintainers typically manage their projects through Git, a distributed version control system that supports collaboration. &lt;/p&gt;

&lt;p&gt;This cheat sheet-style guide provides a quick reference to commands that are useful for working and collaborating in a Git repository. To install and configure Git, be sure to read “&lt;a href="https://www.digitalocean.com/community/tutorials/how-to-contribute-to-open-source-getting-started-with-git"&gt;How To Contribute to Open Source: Getting Started with Git&lt;/a&gt;.”&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How to Use This Guide:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This guide is in cheat sheet format with self-contained command-line snippets.&lt;/li&gt;
&lt;li&gt;Jump to any section that is relevant to the task you are trying to complete.&lt;/li&gt;
&lt;li&gt;When you see &lt;code&gt;&lt;span class="highlight"&gt;highlighted text&lt;/span&gt;&lt;/code&gt; in this guide’s commands, keep in mind that this text should refer to the commits and files in &lt;em&gt;your own&lt;/em&gt; repository.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="set-up-and-initialization"&gt;Set Up and Initialization&lt;/h2&gt;

&lt;p&gt;Check your Git version with the following command, which will also confirm that Git is installed.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;git --version
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can initialize your current working directory as a Git repository with &lt;code&gt;init&lt;/code&gt;.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;git init
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To copy an existing Git repository hosted remotely, you’ll use &lt;code&gt;git clone&lt;/code&gt; with the repo’s URL or server location (in the latter case you will use &lt;code&gt;ssh&lt;/code&gt;).&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;git clone &lt;span class="highlight"&gt;https://www.github.com/username/repo-name&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Show your current Git directory’s remote repository.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;git remote
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For a more verbose output, use the &lt;code&gt;-v&lt;/code&gt; flag.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;git remote -v
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add the Git upstream, which can be a URL or can be hosted on a server (in the latter case, connect with &lt;code&gt;ssh&lt;/code&gt;).&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;git remote add upstream &lt;span class="highlight"&gt;https://www.github.com/username/repo-name&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="staging"&gt;Staging&lt;/h2&gt;

&lt;p&gt;When you’ve modified a file and have marked it to go in your next commit, it is considered to be a staged file.&lt;/p&gt;

&lt;p&gt;Check the status of your Git repository, including files added that are not staged, and files that are staged. &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;git status
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To stage modified files, use the &lt;code&gt;add&lt;/code&gt; command, which you can run multiple times before a commit. If you make subsequent changes that you want included in the next commit, you must run &lt;code&gt;add&lt;/code&gt; again.&lt;/p&gt;

&lt;p&gt;You can specify the specific file with &lt;code&gt;add&lt;/code&gt;.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;git add &lt;span class="highlight"&gt;my_script.py&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With &lt;code&gt;.&lt;/code&gt; you can add all files in the current directory including files that begin with a &lt;code&gt;.&lt;/code&gt;.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;git add .
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can remove a file from staging while retaining changes within your working directory with &lt;code&gt;reset&lt;/code&gt;.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;git reset &lt;span class="highlight"&gt;my_script.py&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="committing"&gt;Committing&lt;/h2&gt;

&lt;p&gt;Once you have staged your updates, you are ready to commit them, which will record changes you have made to the repository.&lt;/p&gt;

&lt;p&gt;To commit staged files, you’ll run the &lt;code&gt;commit&lt;/code&gt; command with your meaningful commit message so that you can track commits. &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;git commit -m "Commit message"
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can condense staging all tracked files with committing them in one step.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;git commit -am "Commit message"
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you need to modify your commit message, you can do so with the &lt;code&gt;--amend&lt;/code&gt; flag.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;git commit --amend -m "New commit message"
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="branches"&gt;Branches&lt;/h2&gt;

&lt;p&gt;A branch in Git is a movable pointer to one of the commits in the repository, it allows you to isolate work and manage feature development and integrations. You can learn more about branches by reading the &lt;a href="https://git-scm.com/book/en/v1/Git-Branching-What-a-Branch-Is"&gt;Git documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;List all current branches with the &lt;code&gt;branch&lt;/code&gt; command. An asterisk (&lt;code&gt;*&lt;/code&gt;) will appear next to your currently active branch.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;git branch
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Create a new branch. You will remain on your currently active branch until you switch to the new one.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;git branch &lt;span class="highlight"&gt;new-branch&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Switch to any existing branch and check it out into your current working directory.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;git checkout &lt;span class="highlight"&gt;another-branch&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can consolidate the creation and checkout of a new branch by using the &lt;code&gt;-b&lt;/code&gt; flag.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;git checkout -b &lt;span class="highlight"&gt;new-branch&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Rename your branch name.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;git branch -m &lt;span class="highlight"&gt;current-branch-name&lt;/span&gt; &lt;span class="highlight"&gt;new-branch-name&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Merge the specified branch’s history into the one you’re currently working in. &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;git merge &lt;span class="highlight"&gt;branch-name&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Abort the merge, in case there are conflicts. &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;git merge --abort
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can also select a particular commit to merge with &lt;code&gt;cherry-pick&lt;/code&gt; with the string that references the specific commit. &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;git cherry-pick &lt;span class="highlight"&gt;f7649d0&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When you have merged a branch and no longer need the branch, you can delete it.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;git branch -d &lt;span class="highlight"&gt;branch-name&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you have not merged a branch to master, but are sure you want to delete it, you can &lt;strong&gt;force&lt;/strong&gt; delete a branch.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;git branch -D &lt;span class="highlight"&gt;branch-name&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="collaborate-and-update"&gt;Collaborate and Update&lt;/h2&gt;

&lt;p&gt;To download changes from another repository, such as the remote upstream, you’ll use &lt;code&gt;fetch&lt;/code&gt;. &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;git fetch &lt;span class="highlight"&gt;upstream&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Merge the fetched commits.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;git merge upstream/master
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Push or transmit your local branch commits to the remote repository branch.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;git push origin master
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Fetch and merge any commits from the tracking remote branch.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;git pull
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="inspecting"&gt;Inspecting&lt;/h2&gt;

&lt;p&gt;Display the commit history for the currently active branch.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;git log
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Show the commits that changed a particular file. This follows the file regardless of file renaming.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;git log --follow &lt;span class="highlight"&gt;my_script.py&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Show the commits that are on one branch and not on the other. This will show commits on &lt;code&gt;&lt;span class="highlight"&gt;a-branch&lt;/span&gt;&lt;/code&gt; that are not on &lt;code&gt;&lt;span class="highlight"&gt;b-branch&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;git log &lt;span class="highlight"&gt;a-branch&lt;/span&gt;..&lt;span class="highlight"&gt;b-branch&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Look at reference logs (&lt;code&gt;reflog&lt;/code&gt;) to see when the tips of branches and other references were last updated within the repository.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;git reflog
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Show any object in Git via its commit string or hash in a more human-readable format.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;git show &lt;span class="highlight"&gt;de754f5&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="show-changes"&gt;Show Changes&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;git diff&lt;/code&gt; command shows changes between commits, branches, and more. You can read more fully about it through the &lt;a href="https://git-scm.com/docs/git-diff"&gt;Git documentation&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Compare modified files that are on the staging area.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;git diff --staged
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Display the diff of what is in &lt;code&gt;&lt;span class="highlight"&gt;a-branch&lt;/span&gt;&lt;/code&gt; but is not in &lt;code&gt;&lt;span class="highlight"&gt;b-branch&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;git diff &lt;span class="highlight"&gt;a-branch&lt;/span&gt;..&lt;span class="highlight"&gt;b-branch&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Show the diff between two specific commits.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;git diff &lt;span class="highlight"&gt;61ce3e6&lt;/span&gt;..&lt;span class="highlight"&gt;e221d9c&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="stashing"&gt;Stashing&lt;/h2&gt;

&lt;p&gt;Sometimes you’ll find that you made changes to some code, but before you finish you have to begin working on something else. You’re not quite ready to commit the changes you have made so far, but you don’t want to lose your work. The &lt;code&gt;git stash&lt;/code&gt; command will allow you to save your local modifications and revert back to the working directory that is in line with the most recent &lt;code&gt;HEAD&lt;/code&gt; commit.&lt;/p&gt;

&lt;p&gt;Stash your current work.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;git stash
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;See what you currently have stashed.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;git stash list
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Your stashes will be named &lt;code&gt;stash@{0}&lt;/code&gt;, &lt;code&gt;stash@{1}&lt;/code&gt;, and so on.&lt;/p&gt;

&lt;p&gt;Show information about a particular stash.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;git stash show stash@{&lt;span class="highlight"&gt;0&lt;/span&gt;}
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To bring the files in a current stash out of the stash while still retaining the stash, use &lt;code&gt;apply&lt;/code&gt;.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;git stash apply stash@{&lt;span class="highlight"&gt;0&lt;/span&gt;}
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you want to bring files out of a stash, and no longer need the stash, use &lt;code&gt;pop&lt;/code&gt;.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;git stash pop stash@{&lt;span class="highlight"&gt;0&lt;/span&gt;}
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you no longer need the files saved in a particular stash, you can &lt;code&gt;drop&lt;/code&gt; the stash.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;git stash drop stash@{&lt;span class="highlight"&gt;0&lt;/span&gt;}
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you have multiple stashes saved and no longer need to use any of them, you can use &lt;code&gt;clear&lt;/code&gt; to remove them.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;git stash clear
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="ignoring-files"&gt;Ignoring Files&lt;/h2&gt;

&lt;p&gt;If you want to keep files in your local Git directory, but do not want to commit them to the project, you can add these files to your &lt;code&gt;.gitignore&lt;/code&gt; file so that they do not cause conflicts.&lt;/p&gt;

&lt;p&gt;Use a text editor such as nano to add files to the &lt;code&gt;.gitignore&lt;/code&gt; file.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nano .gitignore
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To see examples of &lt;code&gt;.gitignore&lt;/code&gt; files, you can look at GitHub’s &lt;a href="https://github.com/github/gitignore"&gt;&lt;code&gt;.gitignore&lt;/code&gt; template repo&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id="rebasing"&gt;Rebasing&lt;/h2&gt;

&lt;p&gt;A rebase allows us to move branches around by changing the commit that they are based on. With rebasing, you can squash or reword commits.&lt;/p&gt;

&lt;p&gt;You can start a rebase by either calling the number of commits you have made that you want to rebase (&lt;code&gt;5&lt;/code&gt; in the case below).&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;git rebase -i HEAD~&lt;span class="highlight"&gt;5&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Alternatively, you can rebase based on a particular commit string or hash.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;git rebase -i &lt;span class="highlight"&gt;074a4e5&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once you have squashed or reworded commits, you can complete the rebase of your branch on top of the latest version of the project’s upstream code. &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;git rebase &lt;span class="highlight"&gt;upstream/master&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To learn more about rebasing and updating, you can read &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-rebase-and-update-a-pull-request"&gt;How To Rebase and Update a Pull Request&lt;/a&gt;, which is also applicable to any type of commit.&lt;/p&gt;

&lt;h2 id="resetting"&gt;Resetting&lt;/h2&gt;

&lt;p&gt;Sometimes, including after a rebase, you need to reset your working tree. You can reset to a particular commit, and &lt;strong&gt;delete all changes&lt;/strong&gt;, with the following command.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;git reset --hard &lt;span class="highlight"&gt;1fc6665&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To force push your last known non-conflicting commit to the origin repository, you’ll need to use &lt;code&gt;--force&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;&lt;span class='warning'&gt;&lt;strong&gt;Warning&lt;/strong&gt;: Force pushing to master is often frowned upon unless there is a really important reason for doing it. Use sparingly when working on your own repositories, and work to avoid this when you’re collaborating.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;git push --force origin master
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To remove local untracked files and subdirectories from the Git directory for a clean working branch, you can use &lt;code&gt;git clean&lt;/code&gt;.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;git clean -f -d
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you need to modify your local repository so that it looks like the current upstream master (that is, there are too many conflicts), you can perform a hard reset.&lt;/p&gt;

&lt;p&gt;&lt;span class='note'&gt;&lt;strong&gt;Note&lt;/strong&gt;: Performing this command will make your local repository look exactly like the upstream. Any commits you have made but that were not pulled into the upstream &lt;strong&gt;will be destroyed&lt;/strong&gt;.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;git reset --hard upstream/master
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;This guide covers some of the more common Git commands you may use when managing repositories and collaborating on software. &lt;/p&gt;

&lt;p&gt;You can learn more about open-source software and collaboration in our &lt;a href="https://www.digitalocean.com/community/tutorial_series/an-introduction-to-open-source"&gt;Introduction to Open Source tutorial series&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.digitalocean.com/community/tutorials/how-to-contribute-to-open-source-getting-started-with-git"&gt;How To Contribute to Open Source: Getting Started with Git&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.digitalocean.com/community/tutorials/how-to-create-a-pull-request-on-github"&gt;How To Create a Pull Request on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.digitalocean.com/community/tutorials/how-to-rebase-and-update-a-pull-request"&gt;How To Rebase and Update a Pull Request&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.digitalocean.com/community/tutorials/how-to-maintain-open-source-software-projects"&gt;How To Maintain Open-Source Software Projects&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are many more commands and variations that you may find useful as part of your work with Git. To learn more about all of your available options, you can run:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;git --help
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To receive useful information. You can also read more about Git and look at Git’s documentation from the &lt;a href="https://git-scm.com/"&gt;official Git website&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/como-instalar-e-configurar-o-gitlab-no-ubuntu-16-04-pt</id>
    <published>2018-10-09T21:50:08Z</published>
    <updated>2018-10-09T21:52:08Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/como-instalar-e-configurar-o-gitlab-no-ubuntu-16-04-pt"/>
    <title>Como Instalar e Configurar o GitLab no Ubuntu 16.04</title>
    <content type="html">&lt;h3 id="introdução"&gt;Introdução&lt;/h3&gt;

&lt;p&gt;O GitLab CE, ou Community Edition, é uma aplicação open source usada principalmente para hospedar repositórios Git, com recursos adicionais relacionados ao desenvolvimento, como rastreamento de problemas. Ele é projetado para ser hospedado usando a sua própria infraestrutura, e fornece flexibilidade na implantação como um repositório interno para sua equipe de desenvolvimento, publicamente como uma forma de interagir com usuários, ou até mesmo aberto como forma de os colaboradores hospedarem seus próprios projetos. &lt;/p&gt;

&lt;p&gt;O projeto do GitLab torna relativamente simples a configuração de uma instância do GitLab em seu próprio hardware com um mecanismo de fácil instalação. Neste guia vamos cobrir como instalar e configurar o GitLab em um servidor Ubuntu 16.04.&lt;/p&gt;

&lt;h2 id="pré-requisitos"&gt;Pré-Requisitos&lt;/h2&gt;

&lt;p&gt;Este tutorial irá assumir que você tem acesso a um novo servidor Ubuntu 16.04. &lt;a href="http://docs.gitlab.com/ee/install/requirements.html#hardware-requirements"&gt;Os requisitos de hardware publicados do GitLab&lt;/a&gt; recomendam a utilização de um servidor com:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;2 núcleos&lt;/li&gt;
&lt;li&gt;4GB de RAM&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Embora você possa substituir algum espaço de swap por RAM, isso não é recomendado. Para este guia assumiremos que você tem os recursos acima, no mínimo.&lt;/p&gt;

&lt;p&gt;Para começar, você vai precisar de um usuário não-root com acesso &lt;code&gt;sudo&lt;/code&gt; configurado no servidor. É também uma boa ideia configurar um firewall básico para fornecer uma camada adicional de segurança. Você pode seguir os passos em nosso tutorial &lt;a href="https://www.digitalocean.com/community/tutorials/configuracao-inicial-de-servidor-com-ubuntu-16-04-pt"&gt;Configuração Inicial de servidor com Ubuntu 16.04&lt;/a&gt; para obter essa configuração.&lt;/p&gt;

&lt;p&gt;Quando tiver satisfeito os pré-requisitos acima, continue para iniciar o procedimento de instalação.&lt;/p&gt;

&lt;h2 id="instalando-as-dependências"&gt;Instalando as Dependências&lt;/h2&gt;

&lt;p&gt;Antes que possamos instalar o GitLab propriamente dito, é importante instalar alguns dos softwares que ele aproveita durante a instalação e que ele usa de forma contínua. Felizmente, todos os softwares necessários podem ser facilmente instalados a partir dos repositórios padrão do Ubuntu.&lt;/p&gt;

&lt;p&gt;Já que esta é a nossa primeira vez usando o &lt;code&gt;apt&lt;/code&gt; durante esta sessão, podemos atualizar o índice de pacotes local e depois instalar as dependências digitando:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt-get update
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo apt-get install ca-certificates curl openssh-server postfix
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Você provavelmente já terá alguns desses softwares instalados. Para a instalação do &lt;code&gt;postfix&lt;/code&gt;, selecione &lt;strong&gt;Internet Site&lt;/strong&gt; quando solicitado. Na próxima tela, entre com o nome de domínio do seu servidor ou seu endereço IP para configurar de que forma o sistema enviará e-mail.&lt;/p&gt;

&lt;h2 id="instalando-o-gitlab"&gt;Instalando o GitLab&lt;/h2&gt;

&lt;p&gt;Agora que as dependências estão instaladas, podemos instalar o GitLab. Este é um processo direto que utiliza um script de instalação para configurar seu sistema com os repositórios do GitLab.&lt;/p&gt;

&lt;p&gt;Vá para o diretório &lt;code&gt;/tmp&lt;/code&gt; e então baixe o script de instalação:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cd /tmp
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;curl -LO https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.deb.sh
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Fique à vontade para examinar o script baixado para assegurar-se de que você esteja confortável com as ações que ele irá tomar. Você pode encontrar uma versão hospedada do script &lt;a href="https://packages.gitlab.com/gitlab/gitlab-ce/install"&gt;aqui&lt;/a&gt;:.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;less /tmp/script.deb.sh
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Quando estiver satisfeito com a segurança do script, execute o instalador:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo bash /tmp/script.deb.sh
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Este script irá configurar seu servidor para utilizar os repositórios mantidos pelo GitLab. Isso lhe permite gerenciar o GitLab com as mesmas ferramentas de gerenciamento de pacotes que você usa para seus outros pacotes de sistema. Quando estiver completo, você pode instalar a aplicação real do GitLab com o &lt;code&gt;apt&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt-get install gitlab-ce
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Isto irá instalar os componentes necessários em seu sistema.&lt;/p&gt;

&lt;h2 id="ajustando-as-regras-de-firewall"&gt;Ajustando as Regras de Firewall&lt;/h2&gt;

&lt;p&gt;Antes de configurar o GitLab, você precisará garantir que suas regras de firewall são permissivas o suficiente para permitir o tráfego web. Se você seguiu o guia vinculado nos pré-requisitos, você terá um firewall &lt;code&gt;ufw&lt;/code&gt; ativado.&lt;/p&gt;

&lt;p&gt;Veja o status atual do seu firewall ativo digitando:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw status
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Status: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere                  
OpenSSH (v6)               ALLOW       Anywhere (v6)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Como você pode ver, as regras atuais permitem o tráfego SSH, mas o acesso a outros serviços está restrito. Como o Gitlab é uma aplicação web, devemos permitir acesso HTTP entrante. Se você tiver um nome de domínio associado com o seu servidor GitLab, o GitLab pode também solicitar e ativar um certificado gratuito TLS/SSL a partir do &lt;a href="https://letsencrypt.org/"&gt;projeto Let's Encrypt&lt;/a&gt; para proteger a instalação. Também queremos permitir o acesso HTTPS nesse caso.&lt;/p&gt;

&lt;p&gt;um vez que o protocolo de mapeamento de portas para HTTP e HTTPS está disponível no arquivo &lt;code&gt;/etc/services&lt;/code&gt;, podemos permitir tais tráfegos pelo nome. Se você ainda não ativou o tráfego do OpenSSH, permita também esse tráfego agora:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw allow http
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo ufw allow https
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo ufw allow OpenSSH
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Se você verificar com o comando &lt;code&gt;ufw status&lt;/code&gt; novamente, você deverá ver o acesso configurado para no mínimo esses dois serviços:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw status
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Status: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere                  
80                         ALLOW       Anywhere                  
443                        ALLOW       Anywhere                  
OpenSSH (v6)               ALLOW       Anywhere (v6)             
80 (v6)                    ALLOW       Anywhere (v6)             
443 (v6)                   ALLOW       Anywhere (v6)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A saída acima indica que a interface web do GitLab estará acessível assim que configurarmos a aplicação.&lt;/p&gt;

&lt;h2 id="editando-o-arquivo-de-configuração-do-gitlab"&gt;Editando o Arquivo de Configuração do GitLab&lt;/h2&gt;

&lt;p&gt;Antes de você poder usar a aplicação, você precisa atualizar um arquivo de configuração e executar um comando de reconfiguração. Primeiro, abra o arquivo de configuração do GitLab:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/gitlab/gitlab.rb
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Perto do topo está a linha de configuração &lt;code&gt;external_url&lt;/code&gt;. Atualize-a para corresponder ao seu próprio domínio ou endereço IP. Se você tem um domínio, altere o &lt;code&gt;http&lt;/code&gt; para &lt;code&gt;https&lt;/code&gt; para que o GitLab redirecione automaticamente os usuários para o site protegido pelo certificado do Let´s Encrypt que estaremos solicitando.&lt;/p&gt;
&lt;div class="code-label " title="/etc/gitlab/gitlab.rb"&gt;/etc/gitlab/gitlab.rb&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;
# If your GitLab server does not have a domain name, you will need to use an IP
# address instead of a domain and keep the protocol as `http`.
external_url 'http&lt;span class="highlight"&gt;s&lt;/span&gt;://&lt;span class="highlight"&gt;seu_dominio&lt;/span&gt;'
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Em seguida, se seu servidor GitLab tem um nome de domínio, pesquise o arquivo pela configuração &lt;code&gt;letsencrypt['enable']&lt;/code&gt;. Descomente a linha e defina-a para &lt;code&gt;true&lt;/code&gt;. Isso dirá ao GitLab para solicitar um certificado Let´s Encrypt para seu domínio GitLab e configurar a aplicação para servir o tráfego com ele.&lt;/p&gt;

&lt;p&gt;Abaixo disso, procure a configuração &lt;code&gt;letsencrypt['contact_emails']&lt;/code&gt;. Esta configuração define uma lista de endereços de e-mail que o projeto Let´s Encrypt pode utilizar para lhe contatar se houver problemas com seu domínio. É uma boa idéia descomentar e preencher isso também para que você saiba de quaisquer problemas:&lt;/p&gt;
&lt;div class="code-label " title="/etc/gitlab/gitlab.rb"&gt;/etc/gitlab/gitlab.rb&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;
letsencrypt['enable'] = &lt;span class="highlight"&gt;true&lt;/span&gt;
letsencrypt['contact_emails'] = ['&lt;span class="highlight"&gt;sammy@seu_domínio.com&lt;/span&gt;']
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Salve e feche o arquivo. Agora, execute o seguinte comando para reconfigurar o GitLab:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo gitlab-ctl reconfigure
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Isso irá inicializar o GitLab utilizando as informações que ele pode encontrar sobre seu servidor. Esse é um processo completamente automatizado, portanto você não vai ter que responder a nenhuma solicitação. Se você ativou a integração com o Let´s Encrypt, um certificado deve ser configurado para o seu domínio.&lt;/p&gt;

&lt;h2 id="realizando-a-configuração-inicial-através-da-interface-web"&gt;Realizando a Configuração Inicial Através da Interface web&lt;/h2&gt;

&lt;p&gt;Agora que o GitLab está executando e o acesso está permitido, podemos realizar algumas configurações iniciais da aplicação através da interface web.&lt;/p&gt;

&lt;h3 id="efetuando-o-login-pela-primeira-vez"&gt;Efetuando o Login pela Primeira Vez&lt;/h3&gt;

&lt;p&gt;Visite o nome de domínio do seu servidor GitLab em seu navegador:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;http://&lt;span class="highlight"&gt;domínio_gitlab_ou_IP&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Se você ativou o Let's Encrypt e usou &lt;code&gt;https&lt;/code&gt; em seu &lt;code&gt;external_url&lt;/code&gt;, você deve ser redirecionado para uma conexão HTTPS segura.&lt;/p&gt;

&lt;p&gt;Em sua primeira visita, você deve ver uma solicitação inicial para definir uma senha para a conta administrativa:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/gitlab_install_1604/gitlab_initial_password2.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Na solicitação inicial de senha, forneça e confirme uma senha segura para a conta administrativa. Clique no botão &lt;strong&gt;Change your password&lt;/strong&gt; quando tiver terminado.&lt;/p&gt;

&lt;p&gt;Você será redirecionado à página convencional de login do GitLab:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/gitlab_install_1604/gitlab_first_signin2.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Aqui, você pode efetuar o login com a senha que você acabou de definir. As credenciais são:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Username: &lt;strong&gt;root&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Password: [a senha que você definiu]&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Entre com esses valores nos campos para os usuários existentes e clique no botão &lt;strong&gt;Sign in&lt;/strong&gt;. Você será autenticado no aplicativo e levado a uma página de entrada que solicitará que você comece a adicionar projetos:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/gitlab_install_1604/landing_page2.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Agora você pode fazer algumas mudanças simples para configurar o GitLab da maneira que você quiser.&lt;/p&gt;

&lt;h3 id="ajustando-suas-configurações-de-perfil"&gt;Ajustando suas Configurações de Perfil&lt;/h3&gt;

&lt;p&gt;Uma das primeiras coisas que você deve fazer após uma nova instalação é colocar o seu perfil na melhor forma. O GitLab seleciona alguns padrões razoáveis, mas estes geralmente não são apropriados quando você começa a usar o software.&lt;/p&gt;

&lt;p&gt;Para fazer as modificações necessárias, clique no ícone do usuário no canto superior direito da interface. No menu suspenso exibido, selecione &lt;strong&gt;Settings&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/gitlab_install_1604/profile_settings_button2.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Você será levado para a seção de perfil ou &lt;strong&gt;Profile&lt;/strong&gt; nas suas configurações:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/gitlab_install_1604/profile_settings2.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Ajuste &lt;strong&gt;Name&lt;/strong&gt; e &lt;strong&gt;Email&lt;/strong&gt; trocando "Administrator" e "&lt;a href="mailto:admin@example.com"&gt;admin@example.com&lt;/a&gt;" para algo mais adequado. O nome que você selecionou será mostrado para os outros usuários, equanto o e-mail será utilizado para detecção padrão do avatar, notificações, ações no Git através da interface, etc. &lt;/p&gt;

&lt;p&gt;Clique no botão &lt;strong&gt;Update Profile setting&lt;/strong&gt; na parte inferior quando terminar:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/gitlab_install_1604/update_profile_settings_button2.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Um e-mail de confirmação será enviado ao endereço que você forneceu.&lt;br&gt;
Siga as instruções no e-mail para confirmar sua conta para que você possa começar a utilização do GitLab.&lt;/p&gt;

&lt;h3 id="alterando-o-nome-da-sua-conta"&gt;Alterando o Nome da Sua Conta&lt;/h3&gt;

&lt;p&gt;A seguir, clique no item &lt;strong&gt;Account&lt;/strong&gt; na barra de menu à esquerda:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/gitlab_install_1604/account_menu_item2.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Aqui, você pode encontrar seu token de API privada ou configurar a autenticação de dois fatores. Contudo, a funcionalidade a qual estamos interessados no momento é a seção &lt;strong&gt;Change username&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Por padrão, à primeira conta administrativa é dado o nome &lt;strong&gt;root&lt;/strong&gt;. Como esse é um nome conhecido de conta, é mais seguro alterar esse nome para um diferente. Você ainda terá privilégios administrativos; a única coisa que irá mudar é o nome:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/gitlab_install_1604/change_username2.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Clique no botão &lt;strong&gt;Update username&lt;/strong&gt; para fazer a alteração:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/gitlab_install_1604/update_username_button2.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Na próxima vez que você fizer o login no GitLab, lembre-se de utilizar seu novo nome de usuário.&lt;/p&gt;

&lt;h3 id="adicionando-uma-chave-ssh-à-sua-conta"&gt;Adicionando uma Chave SSH à sua Conta&lt;/h3&gt;

&lt;p&gt;Em muitos casos, você irá querer usar chaves SSH com Git para interagir com seus projetos GitLab. Para fazer isso, você precisa adicionar sua chave pública à sua conta do GitLab.&lt;/p&gt;

&lt;p&gt;Se você já tem um par de chaves SSH criado em seu &lt;strong&gt;computador local&lt;/strong&gt;, você geralmente pode ver a chave pública digitando:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cat ~/.ssh/id_rsa.pub
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Você deverá ver um grande pedaço de texto, como este:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDMuyMtMl6aWwqBCvQx7YXvZd7bCFVDsyln3yh5/8Pu23LW88VXfJgsBvhZZ9W0rPBGYyzE/TDzwwITvVQcKrwQrvQlYxTVbqZQDlmsC41HnwDfGFXg+QouZemQ2YgMeHfBzy+w26/gg480nC2PPNd0OG79+e7gFVrTL79JA/MyePBugvYqOAbl30h7M1a7EHP3IV5DQUQg4YUq49v4d3AvM0aia4EUowJs0P/j83nsZt8yiE2JEYR03kDgT/qziPK7LnVFqpFDSPC3MR3b8B354E9Af4C/JHgvglv2tsxOyvKupyZonbyr68CqSorO2rAwY/jWFEiArIaVuDiR9YM5 sammy@mydesktop
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Copie esse texto e volte para a página de configurações do perfil na interface web do gitLab.&lt;/p&gt;

&lt;p&gt;Se, em vez disso, você receber uma mensagem parecida como isto, você ainda não tem um par SSH configurado em sua máquina:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;cat: /home/sammy/.ssh/id_rsa.pub: No such file or directory
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Se for o caso, você pode criar um par de chaves SSH digitando:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;ssh-keygen
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Aceite os padrões e, opcionalmente, forneça uma senha para proteger a chave localmente:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Generating public/private rsa key pair.
Enter file in which to save the key (/home/sammy/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/sammy/.ssh/id_rsa.
Your public key has been saved in /home/sammy/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:I8v5/M5xOicZRZq/XRcSBNxTQV2BZszjlWaIHi5chc0 sammy@gitlab.docsthat.work
The key's randomart image is:
+---[RSA 2048]----+
|          ..%o==B|
|           *.E =.|
|        . ++= B  |
|         ooo.o . |
|      . S .o  . .|
|     . + .. .   o|
|      +   .o.o ..|
|       o .++o .  |
|        oo=+     |
+----[SHA256]-----+
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Depois de ter isso, você pode exibir sua chave pública como acima, digitando:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cat ~/.ssh/id_rsa.pub
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDMuyMtMl6aWwqBCvQx7YXvZd7bCFVDsyln3yh5/8Pu23LW88VXfJgsBvhZZ9W0rPBGYyzE/TDzwwITvVQcKrwQrvQlYxTVbqZQDlmsC41HnwDfGFXg+QouZemQ2YgMeHfBzy+w26/gg480nC2PPNd0OG79+e7gFVrTL79JA/MyePBugvYqOAbl30h7M1a7EHP3IV5DQUQg4YUq49v4d3AvM0aia4EUowJs0P/j83nsZt8yiE2JEYR03kDgT/qziPK7LnVFqpFDSPC3MR3b8B354E9Af4C/JHgvglv2tsxOyvKupyZonbyr68CqSorO2rAwY/jWFEiArIaVuDiR9YM5 sammy@mydesktop
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Copie o bloco de texto exibido e volte para as configurações do perfil na interface web do GitLab.&lt;/p&gt;

&lt;p&gt;Clique no item &lt;strong&gt;SSH keys&lt;/strong&gt; no menu à esquerda:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/gitlab_install_1604/ssh_keys_menu_item2.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;No espaço fornecido cole a chave pública que você copiou da sua máquina local. Dê a ela um título descritivo, e clique no botão &lt;strong&gt;Add key&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/gitlab_install_1604/add_ssh_key2.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Agora você deve conseguir gerenciar seus projetos e repositórios do GitLab a partir de sua máquina local sem ter que fornecer suas credenciais de conta do GitLab.&lt;/p&gt;

&lt;h2 id="restringindo-ou-desabilitando-inscrições-públicas-opcional"&gt;Restringindo ou Desabilitando Inscrições Públicas (Opcional)&lt;/h2&gt;

&lt;p&gt;Você deve ter notado que é possível que alguém se inscreva para uma conta quando visitar a página de destino da sua instância do GitLab. Isso pode ser o que você deseja se estiver hospedando um projeto público. No entanto, muitas vezes, configurações mais restritivas são desejáveis.&lt;/p&gt;

&lt;p&gt;Para começar, vá até a área administrativa clicando no ícone de &lt;strong&gt;chave inglesa&lt;/strong&gt; na barra de menu principal na parte superior da página:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/gitlab_install_1604/admin_area_button2.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Na página seguinte, você pode ver uma visão geral da sua instância do GitLab como um todo. Para ajustar as configurações, clique no item &lt;strong&gt;Settings&lt;/strong&gt; na parte inferior do menu à esquerda.&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/gitlab_install_1604/admin_settings_button2.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Você será levado para as configurações globais da sua instância do GitLab. Aqui, você pode ajustar várias configurações que afetam se novos usuários podem se inscrever e qual será o nível de acesso deles.&lt;/p&gt;

&lt;h3 id="desabilitando-inscrições"&gt;Desabilitando Inscrições&lt;/h3&gt;

&lt;p&gt;Se você deseja desabilitar completamente as inscrições (você ainda pode criar manualmente as contas para novos usuários), desça até a seção &lt;strong&gt;Sign-up Restrictions&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Desmarque a caixa de seleção &lt;strong&gt;Sign-up enabled&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/gitlab_install_1604/deselect_sign-ups_enabled.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Role para baixo até o final e clique no botão &lt;strong&gt;Save&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/gitlab_install_1604/save_settings_button2.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;A seção de inscrição agora deve estar removida da página inicial do GitLab.&lt;/p&gt;

&lt;h3 id="restringindo-inscrições-por-domínio"&gt;Restringindo Inscrições Por Domínio&lt;/h3&gt;

&lt;p&gt;Se você estiver usando o GitLab como parte de uma organização que fornece endereços de e-mail associados a um domínio, poderá restringir as inscrições por domínio em vez de desativá-las completamente.&lt;/p&gt;

&lt;p&gt;Na seção &lt;strong&gt;Sign-up Restrictions&lt;/strong&gt;, primeiro selecione a caixa &lt;strong&gt;Send confirmation email on sign-up&lt;/strong&gt; permitindo somente que os usuários façam login depois de confirmarem seus e-mails.&lt;/p&gt;

&lt;p&gt;Em seguida, adicione o seu domínio ou domínios à caixa &lt;strong&gt;Whitelisted domains for sign-ups&lt;/strong&gt;, uma por linha. Você pode utilizar o asterisco "*" para especificar domínios curinga.&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/gitlab_install_1604/restrict_sign-ups_by_domain.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Role para baixo até o final e clique no botão &lt;strong&gt;Salvar&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/gitlab_install_1604/save_settings_button2.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;A seção de inscrição agora deve estar removida da página inicial do GitLab.&lt;/p&gt;

&lt;h3 id="restringindo-a-criação-de-projetos"&gt;Restringindo a Criação de Projetos&lt;/h3&gt;

&lt;p&gt;Por padrão, novos usuários podem criar até 10 projetos. Se você deseja permitir que novos usuários externos tenham visibilidade e participação, mas quer restringir seus acessos ao criar novos projetos, você pode fazer isto na seção &lt;strong&gt;Account and Limit Settings&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Lá dentro, você pode alterar o limite padrão de projetos em &lt;strong&gt;Default projects limit&lt;/strong&gt; para 0 para desativar completamente a criação de projetos por novos usuários.&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/gitlab_install_1604/set_projects_to_zero.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Novos usuários ainda podem ser adicionados a projetos manualmente e terão acesso a projetos internos ou públicos criados por outros usuários.&lt;/p&gt;

&lt;p&gt;Role para baixo até o final e clique no botão &lt;strong&gt;Salvar&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/gitlab_install_1604/save_settings_button2.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Agora, novos usuários poderão criar contas, mas não poderão criar projetos.&lt;/p&gt;

&lt;h2 id="criando-um-cron-job-para-renovar-automaticamente-os-certificados-let-39-s-encrypt"&gt;Criando um Cron Job para Renovar Automaticamente os Certificados Let's Encrypt&lt;/h2&gt;

&lt;p&gt;Por padrão, os certificados Let's Encrypt são válidos por 90 dias. Se você ativou o Let's Encrypt para o seu domínio do GitLab anteriormente, você precisará garantir que seus certificados sejam renovados regularmente para evitar interrupções no serviço. O GitLab fornece o comando &lt;code&gt;gitlab-ctl renew-le-certs&lt;/code&gt; para solicitar novos certificados quando seus ativos atuais se aproximarem da expiração.&lt;/p&gt;

&lt;p&gt;Para automatizar este processo, podemos criar um cron job para executar automaticamente este comando regularmente. O comando somente irá renovar o certificado quando ele estiver perto da expiração, para que possamos executá-lo com segurança regularmente&lt;/p&gt;

&lt;p&gt;Para começar, crie  e abra um arquivo em &lt;code&gt;/etc/cron.daily/gitlab-le&lt;/code&gt; em seu editor de textos:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/cron.daily/gitlab-le
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Dentro dele, cole o seguinte script:&lt;/p&gt;
&lt;div class="code-label " title="/etc/cron.daily/gitlab-le"&gt;/etc/cron.daily/gitlab-le&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;
#!/bin/bash

set -e

/usr/bin/gitlab-ctl renew-le-certs &amp;gt; /dev/null
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Salve e feche o arquivo quando tiver terminado.&lt;/p&gt;

&lt;p&gt;Marque o arquivo como executável digitando:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo chmod +x /etc/cron.daily/gitlab-le
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Agora, o GitLab deve verificar automaticamente todos os dias se seu certificado Let's Encrypt precisa ser renovado. Em caso afirmativo, o comando renovará o certificado automaticamente.&lt;/p&gt;

&lt;h2 id="conclusão"&gt;Conclusão&lt;/h2&gt;

&lt;p&gt;Agora você deve ter uma instância do GitLab em funcionamento hospedada em seu próprio servidor. Você pode começar a importar ou criar novos projetos e configurar o nível apropriado de acesso para sua equipe. O GitLab está regularmente adicionando recursos e atualizando sua plataforma, por isso confira a página inicial do projeto para se manter atualizado sobre quaisquer melhorias ou avisos importantes.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Por Justin Ellingwood&lt;/em&gt;&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-test-ansible-roles-with-molecule-on-ubuntu-18-04</id>
    <published>2018-09-18T18:21:46Z</published>
    <updated>2018-10-02T19:56:24Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-test-ansible-roles-with-molecule-on-ubuntu-18-04"/>
    <title>How To Test Ansible Roles with Molecule on Ubuntu 18.04</title>
    <content type="html">&lt;p&gt;&lt;em&gt;The author selected the &lt;a href="https://www.brightfunds.org/organizations/mozilla-foundation"&gt;Mozilla Foundation&lt;/a&gt; to receive a donation as part of the &lt;a href="https://do.co/w4do-cta"&gt;Write for DOnations&lt;/a&gt; program.&lt;/em&gt;&lt;/p&gt;

&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;Unit testing in &lt;a href="https://www.ansible.com/"&gt;Ansible&lt;/a&gt; is key to making sure roles function as intended. &lt;a href="https://github.com/metacloud/molecule/"&gt;Molecule&lt;/a&gt; makes this process easier by allowing you to specify scenarios that test roles against different environments. Using Ansible under the hood, Molecule offloads roles to a provisioner that deploys the role in a configured environment and calls a verifier (such as &lt;a href="https://github.com/philpep/testinfra"&gt;Testinfra&lt;/a&gt;) to check for configuration drift. This ensures that your role has made all of the expected changes to the environment in that particular scenario.&lt;/p&gt;

&lt;p&gt;In this guide, you will build an Ansible role that deploys &lt;a href="https://httpd.apache.org/"&gt;Apache&lt;/a&gt; to a host and configures &lt;a href="https://firewalld.org/"&gt;firewalld&lt;/a&gt; on CentOS 7. To test that this role works as intended, you will create a test in Molecule using &lt;a href="https://www.docker.com/"&gt;Docker&lt;/a&gt; as a driver and Testinfra, a Python library for testing the state of servers. Molecule will provision Docker containers to test the role and Testinfra will verify that the server has been configured as intended. When you're finished, you'll be able to create multiple test cases for builds across environments and run these tests using Molecule.&lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;Before you begin this guide you'll need the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One Ubuntu 18.04 server. Follow the steps in the &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-18-04"&gt;Initial Server Setup with Ubuntu 18.04&lt;/a&gt; guide to create a non-root sudo user, and make sure you can connect to the server without a password.&lt;/li&gt;
&lt;li&gt;Docker installed on your server. Follow Steps 1 and 2 in &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-and-use-docker-on-ubuntu-18-04"&gt;How To Install and Use Docker on Ubuntu 18.04&lt;/a&gt;, including adding your non-root user to the &lt;code&gt;docker&lt;/code&gt; group.&lt;/li&gt;
&lt;li&gt;Python 3 and &lt;code&gt;venv&lt;/code&gt; installed and configured on your server. Follow &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-python-3-and-set-up-a-programming-environment-on-an-ubuntu-18-04-server"&gt;How To Install Python 3 and Set Up a Programming Environment on an Ubuntu 18.04 Server &lt;/a&gt; for guidance.&lt;/li&gt;
&lt;li&gt;Familiarity with Ansible playbooks. For review, see &lt;a href="https://www.digitalocean.com/community/tutorials/configuration-management-101-writing-ansible-playbooks"&gt;Configuration Management 101: Writing Ansible Playbooks&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="step-1-—-preparing-the-environment"&gt;Step 1 — Preparing the Environment&lt;/h2&gt;

&lt;p&gt;If you've followed the prerequisites, you should have Python 3, &lt;code&gt;venv&lt;/code&gt;, and Docker installed and correctly configured. Let's begin by creating a virtual environment to test Ansible with Molecule.&lt;/p&gt;

&lt;p&gt;Start by logging in as your non-root user and creating a new virtual environment:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;python3 -m venv &lt;span class="highlight"&gt;my_env&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Activate it to ensure that your actions are restricted to that environment:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;source &lt;span class="highlight"&gt;my_env&lt;/span&gt;/bin/activate
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, in your activated environment, install the &lt;code&gt;wheel&lt;/code&gt; package, which provides the &lt;code&gt;bdist_wheel&lt;/code&gt; &lt;code&gt;setuptools&lt;/code&gt; extension that &lt;code&gt;pip&lt;/code&gt; uses to install Ansible:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="(my_env) sammy@ubuntu:~$"&gt;python3 -m pip install wheel
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can now install &lt;code&gt;molecule&lt;/code&gt; and &lt;code&gt;docker&lt;/code&gt; with &lt;code&gt;pip&lt;/code&gt;. Ansible will be automatically installed as a dependency for Molecule:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="(my_env) sammy@ubuntu:~$"&gt;python3 -m pip install molecule docker
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here is what each of these packages will do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;molecule&lt;/code&gt;: This is the main Molecule package that you will use to test roles. Installing &lt;code&gt;molecule&lt;/code&gt; automatically installs Ansible, along with other dependencies, and enables the use of Ansible playbooks to execute roles and tests.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;docker&lt;/code&gt;: This Python library is used by Molecule to interface with Docker. You will need this since you're using Docker as a driver.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Next, let's create a role in Molecule.&lt;/p&gt;

&lt;h2 id="step-2-—-creating-a-role-in-molecule"&gt;Step 2 — Creating a Role in Molecule&lt;/h2&gt;

&lt;p&gt;With your environment set up, you can use Molecule to create a basic role that you will use to test an installation of Apache. This role will create the directory structure and some initial tests, and specify Docker as the driver so that Molecule uses Docker to run its tests.&lt;/p&gt;

&lt;p&gt;Create a new role called &lt;code&gt;ansible-apache&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="(my_env) sammy@ubuntu:~$"&gt;molecule init role -r ansible-apache -d docker
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;-r&lt;/code&gt; flag specifies the name of the role while &lt;code&gt;-d&lt;/code&gt; specifies the driver, which provisions the hosts for Molecule to use in testing.&lt;/p&gt;

&lt;p&gt;Change into the directory of the newly created role:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="(my_env) sammy@ubuntu:~$"&gt;cd ansible-apache
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Test the default role to check if Molecule has been set up properly:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="(my_env) sammy@ubuntu:~/ansible-apache$"&gt;molecule test
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will see output that lists each of the default test actions. Before starting the test, Molecule validates the configuration file &lt;code&gt;molecule.yml&lt;/code&gt; to make sure everything is in order. It also prints this test matrix, which specifies the order of test actions:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;--&amp;gt; Validating schema /home/sammy/ansible-apache/molecule/default/molecule.yml.
Validation completed successfully.
--&amp;gt; Test matrix

└── default
    ├── lint
    ├── destroy
    ├── dependency
    ├── syntax
    ├── create
    ├── prepare
    ├── converge
    ├── idempotence
    ├── side_effect
    ├── verify
    └── destroy
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We will discuss each test action in detail once you've created your role and customized your tests. For now, pay attention to the &lt;code&gt;PLAY_RECAP&lt;/code&gt; for each test, and be sure that none of the default actions returns a &lt;code&gt;failed&lt;/code&gt; status. For example, the &lt;code&gt;PLAY_RECAP&lt;/code&gt; for the default &lt;code&gt;'create'&lt;/code&gt; action should look like this:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;...
PLAY RECAP *********************************************************************
localhost                  : ok=5    changed=4    unreachable=0    failed=0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let's move on to modifying the role to configure Apache and firewalld.&lt;/p&gt;

&lt;h2 id="step-3-—-configuring-apache-and-firewalld"&gt;Step 3 — Configuring Apache and Firewalld&lt;/h2&gt;

&lt;p&gt;To configure Apache and firewalld, you will create a tasks file for the role, specifying packages to install and services to enable. These details will be extracted from a variables file and template that you will use to replace the default Apache index page.&lt;/p&gt;

&lt;p&gt;Still in the &lt;code&gt;ansible-apache&lt;/code&gt; directory, create a tasks file for the role using &lt;code&gt;nano&lt;/code&gt; or your favorite text editor:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="(my_env) sammy@ubuntu:~/ansible-apache$"&gt;nano tasks/main.yml
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You'll see that the file already exists. Delete what's there and replace it with the following code to install the required packages and enable the correct services, HTML defaults, and firewall settings:&lt;/p&gt;
&lt;div class="code-label " title="~/ansible-apache/tasks/main.yml"&gt;~/ansible-apache/tasks/main.yml&lt;/div&gt;&lt;pre class="code-pre yaml"&gt;&lt;code langs=""&gt;---
- name: "Ensure required packages are present"
  yum:
    name: "{{ pkg_list }}"
    state: present

- name: "Ensure latest index.html is present"
  template:
    src: index.html.j2
    dest: /var/www/html/index.html

- name: "Ensure httpd service is started and enabled"
  service:
    name: "{{ item }}"
    state: started
    enabled: true
  with_items: "{{ svc_list }}"

- name: "Whitelist http in firewalld"
  firewalld:
    service: http
    state: enabled
    permanent: true
    immediate: true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This playbook includes 4 tasks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;"Ensure required packages are present"&lt;/code&gt;: This task will install the packages listed in the variables file under &lt;code&gt;pkg_list&lt;/code&gt;. The variables file will be located at &lt;code&gt;~/ansible-apache/vars/main.yml&lt;/code&gt; and you will create it at the end of this step.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;"Ensure latest index.html is present"&lt;/code&gt;: This task will copy a template page, &lt;code&gt;index.html.j2&lt;/code&gt;, and paste it over the default index file, &lt;code&gt;/var/www/html/index.html&lt;/code&gt;, generated by Apache. You will also create the new template in this step.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;"Ensure httpd service is started and enabled"&lt;/code&gt;: This task will start and enable the services listed in &lt;code&gt;svc_list&lt;/code&gt; in the variables file.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;"Whitelist http in firewalld"&lt;/code&gt;: This task will whitelist the &lt;code&gt;http&lt;/code&gt; service in &lt;code&gt;firewalld&lt;/code&gt;. Firewalld is a complete firewall solution present by default on CentOS servers. For the &lt;code&gt;http&lt;/code&gt; service to work, you will need to expose the required ports. Instructing &lt;code&gt;firewalld&lt;/code&gt; to whitelist a service ensures that it whitelists all of the ports that the service requires.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Save and close the file when you are finished.&lt;/p&gt;

&lt;p&gt;Next, let's create a &lt;code&gt;templates&lt;/code&gt; directory for the &lt;code&gt;index.html.j2&lt;/code&gt; template page:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="(my_env) sammy@ubuntu:~/ansible-apache$"&gt;mkdir templates
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Create the page itself:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="(my_env) sammy@ubuntu:~/ansible-apache$"&gt;nano templates/index.html.j2
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Paste in the following boilerplate code:&lt;/p&gt;
&lt;div class="code-label " title="~/ansible-apache/templates/index.html.j2"&gt;~/ansible-apache/templates/index.html.j2&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-html"&gt;&amp;lt;div style="text-align: center"&amp;gt;
    &amp;lt;h2&amp;gt;Managed by Ansible&amp;lt;/h2&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save and close the file.&lt;/p&gt;

&lt;p&gt;The final step in completing the role is writing the variables file, which provides the names of packages and services to our main role playbook:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="(my_env) sammy@ubuntu:~/ansible-apache$"&gt;nano vars/main.yml
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Paste over the default content with the following code, which specifies &lt;code&gt;pkg_list&lt;/code&gt; and &lt;code&gt;svc_list&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-label " title="~/ansible-apache/vars/main.yml"&gt;~/ansible-apache/vars/main.yml&lt;/div&gt;&lt;pre class="code-pre yaml"&gt;&lt;code langs=""&gt;---
pkg_list:
  - httpd
  - firewalld
svc_list:
  - httpd
  - firewalld
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These lists contain the following information:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;pkg_list&lt;/code&gt;: This contains the names of the packages that the role will install: &lt;code&gt;httpd&lt;/code&gt; and &lt;code&gt;firewalld&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;svc_list&lt;/code&gt;: This contains the names of the services that the role will start and enable: &lt;code&gt;httpd&lt;/code&gt; and &lt;code&gt;firewalld&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;span class='note'&gt;&lt;strong&gt;Note:&lt;/strong&gt; Make sure that your variables file doesn't have any blank lines or your test will fail during linting.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;Now that you've finished creating your role, let's configure Molecule to test if it works as intended.&lt;/p&gt;

&lt;h2 id="step-4-—-modifying-the-role-for-running-tests"&gt;Step 4 — Modifying the Role for Running Tests&lt;/h2&gt;

&lt;p&gt;In our case, configuring Molecule involves modifying the Molecule configuration file &lt;code&gt;molecule.yml&lt;/code&gt; to add platform specifications. Because you're testing a role that configures and starts the &lt;code&gt;httpd&lt;/code&gt; systemd service, you will need to use an image with systemd configured and privileged mode enabled. For this tutorial, you will use the &lt;code&gt;milcom/centos7-systemd&lt;/code&gt; image &lt;a href="https://hub.docker.com/r/milcom/centos7-systemd/"&gt;available on Docker Hub&lt;/a&gt;. Privileged mode allows containers to run with almost all of the capabilities of their host machine.&lt;/p&gt;

&lt;p&gt;Let's edit &lt;code&gt;molecule.yml&lt;/code&gt; to reflect these changes:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="(my_env) sammy@ubuntu:~/ansible-apache$"&gt;nano molecule/default/molecule.yml
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add the highlighted platform information:&lt;/p&gt;
&lt;div class="code-label " title="~/ansible-apache/molecule/default/molecule.yml"&gt;~/ansible-apache/molecule/default/molecule.yml&lt;/div&gt;&lt;pre class="code-pre yaml"&gt;&lt;code langs=""&gt;---
dependency:
  name: galaxy
driver:
  name: docker
lint:
  name: yamllint
platforms:
  &lt;span class="highlight"&gt;- name: centos7&lt;/span&gt;
    &lt;span class="highlight"&gt;image: milcom/centos7-systemd&lt;/span&gt;
    &lt;span class="highlight"&gt;privileged: true&lt;/span&gt;
provisioner:
  name: ansible
  lint:
    name: ansible-lint
scenario:
  name: default
verifier:
  name: testinfra
  lint:
    name: flake8
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save and close the file when you are done.&lt;/p&gt;

&lt;p&gt;Now that you've successfully configured the test environment, let's move on to writing the test cases that Molecule will run against your container after executing the role.&lt;/p&gt;

&lt;h2 id="step-5-—-writing-test-cases"&gt;Step 5 — Writing Test Cases&lt;/h2&gt;

&lt;p&gt;In the test for this role, you will check the following conditions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;That the &lt;code&gt;httpd&lt;/code&gt; and &lt;code&gt;firewalld&lt;/code&gt; packages are installed.&lt;/li&gt;
&lt;li&gt;That the &lt;code&gt;httpd&lt;/code&gt; and &lt;code&gt;firewalld&lt;/code&gt; services are running and enabled.&lt;/li&gt;
&lt;li&gt;That the &lt;code&gt;http&lt;/code&gt; service is enabled in your firewall settings.&lt;/li&gt;
&lt;li&gt;That &lt;code&gt;index.html&lt;/code&gt; contains the same data specified in your template file.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If all of these tests pass, then the role works as intended.&lt;/p&gt;

&lt;p&gt;To write the test cases for these conditions, let's edit the default tests in &lt;code&gt;~/ansible-apache/molecule/default/tests/test_default.py&lt;/code&gt;. Using Testinfra, we will write the test cases as Python functions that use Molecule classes.&lt;/p&gt;

&lt;p&gt;Open &lt;code&gt;test_default.py&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="(my_env) sammy@ubuntu:~/ansible-apache$"&gt;nano molecule/default/tests/test_default.py
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Delete the contents of the file so that you can write the tests from scratch.&lt;/p&gt;

&lt;p&gt;&lt;span class='note'&gt;&lt;strong&gt;Note:&lt;/strong&gt; As you write your tests, make sure that they are separated by two new lines or they will fail.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;Start by importing the required Python modules:&lt;/p&gt;
&lt;div class="code-label " title="~/ansible-apache/molecule/default/tests/test_default.py"&gt;~/ansible-apache/molecule/default/tests/test_default.py&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-python"&gt;import os
import pytest

import testinfra.utils.ansible_runner
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These modules include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;os&lt;/code&gt;: This built-in Python module enables operating-system-dependent functionality, making it possible for Python to interface with the underlying operating system.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pytest&lt;/code&gt;: The &lt;a href="https://docs.pytest.org/en/latest/"&gt;&lt;code&gt;pytest&lt;/code&gt;&lt;/a&gt; module enables test writing.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;testinfra.utils.ansible_runner&lt;/code&gt;: This Testinfra module uses &lt;a href="https://testinfra.readthedocs.io/en/latest/backends.html#ansible"&gt;Ansible as the backend&lt;/a&gt; for command execution.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Under the module imports, add the following code, which uses the Ansible backend to return the current host instance:&lt;/p&gt;
&lt;div class="code-label " title="~/ansible-apache/molecule/default/tests/test_default.py"&gt;~/ansible-apache/molecule/default/tests/test_default.py&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-python"&gt;...
testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
    os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('all')
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With your test file configured to use the Ansible backend, let's write unit tests to test the state of the host.&lt;/p&gt;

&lt;p&gt;The first test will ensure that &lt;code&gt;httpd&lt;/code&gt; and &lt;code&gt;firewalld&lt;/code&gt; are installed:&lt;/p&gt;
&lt;div class="code-label " title="~/ansible-apache/molecule/default/tests/test_default.py"&gt;~/ansible-apache/molecule/default/tests/test_default.py&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-python"&gt;...

@pytest.mark.parametrize('pkg', [
  'httpd',
  'firewalld'
])
def test_pkg(host, pkg):
    package = host.package(pkg)

    assert package.is_installed
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The test begins with the &lt;a href="https://docs.pytest.org/en/latest/parametrize.html#pytest-mark-parametrize-parametrizing-test-functions"&gt;&lt;code&gt;pytest.mark.parametrize&lt;/code&gt; decorator&lt;/a&gt;, which allows us to parameterize the arguments for the test. This first test will take &lt;code&gt;test_pkg&lt;/code&gt; as a parameter to test for the presence of the &lt;code&gt;httpd&lt;/code&gt; and &lt;code&gt;firewalld&lt;/code&gt; packages.&lt;/p&gt;

&lt;p&gt;The next test checks whether or not &lt;code&gt;httpd&lt;/code&gt; and &lt;code&gt;firewalld&lt;/code&gt; are running and enabled. It takes &lt;code&gt;test_svc&lt;/code&gt; as a parameter:&lt;/p&gt;
&lt;div class="code-label " title="~/ansible-apache/molecule/default/tests/test_default.py"&gt;~/ansible-apache/molecule/default/tests/test_default.py&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-python"&gt;...

@pytest.mark.parametrize('svc', [
  'httpd',
  'firewalld'
])
def test_svc(host, svc):
    service = host.service(svc)

    assert service.is_running
    assert service.is_enabled
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The last test checks that the files and contents passed to &lt;code&gt;parametrize()&lt;/code&gt; exist. If the file isn't created by your role and the content isn't set properly, &lt;code&gt;assert&lt;/code&gt; will return &lt;code&gt;False&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-label " title="~/ansible-apache/molecule/default/tests/test_default.py"&gt;~/ansible-apache/molecule/default/tests/test_default.py&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-python"&gt;...

@pytest.mark.parametrize('file, content', [
  ("/etc/firewalld/zones/public.xml", "&amp;lt;service name=\"http\"/&amp;gt;"),
  ("/var/www/html/index.html", "Managed by Ansible")
])
def test_files(host, file, content):
    file = host.file(file)

    assert file.exists
    assert file.contains(content)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In each test, &lt;code&gt;assert&lt;/code&gt; will return &lt;code&gt;True&lt;/code&gt; or &lt;code&gt;False&lt;/code&gt; depending on the test result.&lt;/p&gt;

&lt;p&gt;The finished file looks like this:&lt;/p&gt;
&lt;div class="code-label " title="~/ansible-apache/molecule/default/tests/test_default.py"&gt;~/ansible-apache/molecule/default/tests/test_default.py&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-python"&gt;import os
import pytest

import testinfra.utils.ansible_runner

testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
    os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('all')


@pytest.mark.parametrize('pkg', [
  'httpd',
  'firewalld'
])
def test_pkg(host, pkg):
    package = host.package(pkg)

    assert package.is_installed


@pytest.mark.parametrize('svc', [
  'httpd',
  'firewalld'
])
def test_svc(host, svc):
    service = host.service(svc)

    assert service.is_running
    assert service.is_enabled


@pytest.mark.parametrize('file, content', [
  ("/etc/firewalld/zones/public.xml", "&amp;lt;service name=\"http\"/&amp;gt;"),
  ("/var/www/html/index.html", "Managed by Ansible")
])
def test_files(host, file, content):
    file = host.file(file)

    assert file.exists
    assert file.contains(content)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that you've specified your test cases, let's test the role.&lt;/p&gt;

&lt;h2 id="step-6-—-testing-the-role-with-molecule"&gt;Step 6 — Testing the Role with Molecule&lt;/h2&gt;

&lt;p&gt;Once you initiate the test, Molecule will execute the actions you defined in your scenario. Let's now run the default &lt;code&gt;molecule&lt;/code&gt; scenario again, executing the actions in the default test sequence while looking more closely at each.&lt;/p&gt;

&lt;p&gt;Run the test for the default scenario again:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="(my_env) sammy@ubuntu:~/ansible-apache$"&gt;molecule test
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will initiate the test run. The initial output prints the default test matrix:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;--&amp;gt; Validating schema /home/sammy/ansible-apache/molecule/default/molecule.yml.
Validation completed successfully.
--&amp;gt; Test matrix

└── default
    ├── lint
    ├── destroy
    ├── dependency
    ├── syntax
    ├── create
    ├── prepare
    ├── converge
    ├── idempotence
    ├── side_effect
    ├── verify
    └── destroy
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let's go through each test action and the expected output, starting with linting.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;linting&lt;/em&gt; action executes &lt;code&gt;yamllint&lt;/code&gt;, &lt;code&gt;flake8&lt;/code&gt;, and &lt;code&gt;ansible-lint&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;yamllint&lt;/code&gt;: This linter is executed on all YAML files present in the role directory.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;flake8&lt;/code&gt;: This Python code linter checks tests created for Testinfra.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ansible-lint&lt;/code&gt;: This linter for Ansible playbooks is executed in all scenarios.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;...
--&amp;gt; Scenario: 'default'
--&amp;gt; Action: 'lint'
--&amp;gt; Executing Yamllint on files found in /home/sammy/ansible-apache/...
Lint completed successfully.
--&amp;gt; Executing Flake8 on files found in /home/sammy/ansible-apache/molecule/default/tests/...
Lint completed successfully.
--&amp;gt; Executing Ansible Lint on /home/sammy/ansible-apache/molecule/default/playbook.yml...
Lint completed successfully.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The next action, &lt;em&gt;destroy&lt;/em&gt;, is executed using the &lt;code&gt;destroy.yml&lt;/code&gt; file. This is done to test our role on a newly created container.&lt;/p&gt;

&lt;p&gt;By default, destroy is called twice: at the start of the test run, to delete any pre-existing containers, and at the end, to delete the newly created container:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;...
--&amp;gt; Scenario: 'default'
--&amp;gt; Action: 'destroy'

    PLAY [Destroy] *****************************************************************

    TASK [Destroy molecule instance(s)] ********************************************
    changed: [localhost] =&amp;gt; (item=None)
    changed: [localhost]

    TASK [Wait for instance(s) deletion to complete] *******************************
    ok: [localhost] =&amp;gt; (item=None)
    ok: [localhost]

    TASK [Delete docker network(s)] ************************************************
    skipping: [localhost]

    PLAY RECAP *********************************************************************
    localhost                  : ok=2    changed=1    unreachable=0    failed=0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After the destroy action is complete, the test will move on to &lt;em&gt;dependency&lt;/em&gt;. This action allows you to pull dependencies from &lt;a href="https://galaxy.ansible.com/"&gt;&lt;code&gt;ansible-galaxy&lt;/code&gt;&lt;/a&gt; if your role requires them. In this case, our role does not:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;...
--&amp;gt; Scenario: 'default'
--&amp;gt; Action: 'dependency'
Skipping, missing the requirements file.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The next test action is a &lt;em&gt;syntax&lt;/em&gt; check, which is executed on the default &lt;code&gt;playbook.yml&lt;/code&gt; playbook. It works in a similar way to the &lt;code&gt;--syntax-check&lt;/code&gt; flag in the command &lt;code&gt;ansible-playbook --syntax-check playbook.yml&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;...
--&amp;gt; Scenario: 'default'
--&amp;gt; Action: 'syntax'

    playbook: /home/sammy/ansible-apache/molecule/default/playbook.yml
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, the test moves on to the &lt;em&gt;create&lt;/em&gt; action. This uses the &lt;code&gt;create.yml&lt;/code&gt; file in your role's Molecule directory to create a Docker container with your specifications:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;...

--&amp;gt; Scenario: 'default'
--&amp;gt; Action: 'create'

    PLAY [Create] ******************************************************************

    TASK [Log into a Docker registry] **********************************************
    skipping: [localhost] =&amp;gt; (item=None)
    skipping: [localhost]

    TASK [Create Dockerfiles from image names] *************************************
    changed: [localhost] =&amp;gt; (item=None)
    changed: [localhost]

    TASK [Discover local Docker images] ********************************************
    ok: [localhost] =&amp;gt; (item=None)
    ok: [localhost]

    TASK [Build an Ansible compatible image] ***************************************
    changed: [localhost] =&amp;gt; (item=None)
    changed: [localhost]

    TASK [Create docker network(s)] ************************************************
    skipping: [localhost]

    TASK [Create molecule instance(s)] *********************************************
    changed: [localhost] =&amp;gt; (item=None)
    changed: [localhost]

    TASK [Wait for instance(s) creation to complete] *******************************
    changed: [localhost] =&amp;gt; (item=None)
    changed: [localhost]

    PLAY RECAP *********************************************************************
    localhost                  : ok=5    changed=4    unreachable=0    failed=0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After create, the test moves on to the &lt;em&gt;prepare&lt;/em&gt; action. This action executes the prepare playbook, which brings the host to a specific state before running converge. This is useful if your role requires a pre-configuration of the system before the role is executed. Again, this does not apply to our role:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;...
--&amp;gt; Scenario: 'default'
--&amp;gt; Action: 'prepare'
Skipping, prepare playbook not configured.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After prepare, the &lt;em&gt;converge&lt;/em&gt; action executes your role on the container by running the &lt;code&gt;playbook.yml&lt;/code&gt; playbook. If multiple platforms are configured in the &lt;code&gt;molecule.yml&lt;/code&gt; file, Molecule will converge on all of these:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;...
--&amp;gt; Scenario: 'default'
--&amp;gt; Action: 'converge'

    PLAY [Converge] ****************************************************************

    TASK [Gathering Facts] *********************************************************
    ok: [centos7]

    TASK [ansible-apache : Ensure required packages are present] *******************
    changed: [centos7]

    TASK [ansible-apache : Ensure latest index.html is present] ********************
    changed: [centos7]

    TASK [ansible-apache : Ensure httpd service is started and enabled] ************
    changed: [centos7] =&amp;gt; (item=httpd)
    changed: [centos7] =&amp;gt; (item=firewalld)

    TASK [ansible-apache : Whitelist http in firewalld] ****************************
    changed: [centos7]

    PLAY RECAP *********************************************************************
    centos7                    : ok=5    changed=4    unreachable=0    failed=0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After coverge, the test moves on to &lt;em&gt;idempotence&lt;/em&gt;. This action tests the playbook for idempotence to make sure no unexpected changes are made in multiple runs:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;...
--&amp;gt; Scenario: 'default'
--&amp;gt; Action: 'idempotence'
Idempotence completed successfully.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The next test action is the &lt;em&gt;side-effect&lt;/em&gt; action. This lets you produce situations in which you'll be able to test more things, like HA failover. By default, Molecule doesn't configure a side-effect playbook and the task is skipped:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;...
--&amp;gt; Scenario: 'default'
--&amp;gt; Action: 'side_effect'
Skipping, side effect playbook not configured.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Molecule will then run the &lt;em&gt;verifier&lt;/em&gt; action using the default verifier, Testinfra. This action executes the tests you wrote earlier in &lt;code&gt;test_default.py&lt;/code&gt;. If all the tests pass successfully, you will see a success message and Molecule will proceed to the next step:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;...
--&amp;gt; Scenario: 'default'
--&amp;gt; Action: 'verify'
--&amp;gt; Executing Testinfra tests found in /home/sammy/ansible-apache/molecule/default/tests/...
    ============================= test session starts ==============================
    platform linux -- Python 3.6.5, pytest-3.7.3, py-1.5.4, pluggy-0.7.1
    rootdir: /home/sammy/ansible-apache/molecule/default, inifile:
    plugins: testinfra-1.14.1
collected 6 items

    tests/test_default.py ......                                             [100%]

    ========================== 6 passed in 41.05 seconds ===========================
&lt;span class="highlight"&gt;Verifier completed successfully.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, Molecule &lt;em&gt;destroys&lt;/em&gt; the instances completed during the test and deletes the network assigned to those instances:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;...
--&amp;gt; Scenario: 'default'
--&amp;gt; Action: 'destroy'

    PLAY [Destroy] *****************************************************************

    TASK [Destroy molecule instance(s)] ********************************************
    changed: [localhost] =&amp;gt; (item=None)
    changed: [localhost]

    TASK [Wait for instance(s) deletion to complete] *******************************
    changed: [localhost] =&amp;gt; (item=None)
    changed: [localhost]

    TASK [Delete docker network(s)] ************************************************
    skipping: [localhost]

    PLAY RECAP *********************************************************************
    localhost                  : ok=2    changed=2    unreachable=0    failed=0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The test actions are now complete, verifying that your role worked as intended.&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;In this article you created an Ansible role to install and configure Apache and firewalld. You then wrote unit tests with Testinfra that Molecule used to assert that the role ran successfully. &lt;/p&gt;

&lt;p&gt;You can use the same basic method for highly complex roles, and automate testing using a CI pipeline as well. Molecule is a highly configurable tool that can be used to test roles with any providers that Ansible supports, not just Docker. It's also possible to automate testing against your own infrastructure, making sure that your roles are always up-to-date and functional. The official &lt;a href="https://molecule.readthedocs.io/en/latest/"&gt;Molecule documentation&lt;/a&gt; is the best resource for learning how to use Molecule.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-provision-and-manage-remote-docker-hosts-with-docker-machine-on-ubuntu-18-04</id>
    <published>2018-10-01T20:06:38Z</published>
    <updated>2018-10-02T21:18:08Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-provision-and-manage-remote-docker-hosts-with-docker-machine-on-ubuntu-18-04"/>
    <title>How To Provision and Manage Remote Docker Hosts with Docker Machine on Ubuntu 18.04</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://docs.docker.com/machine/"&gt;Docker Machine&lt;/a&gt; is a tool that makes it easy to provision and manage multiple Docker hosts remotely from your personal computer. Such servers are commonly referred to as Dockerized hosts and are used to run Docker containers.&lt;/p&gt;

&lt;p&gt;While Docker Machine can be installed on a local or a remote system, the most common approach is to install it on your local computer (native installation or virtual machine) and use it to provision Dockerized remote servers.&lt;/p&gt;

&lt;p&gt;Though Docker Machine can be installed on most Linux distributions as well as macOS and Windows, in this tutorial, you'll install it on your local machine running Ubuntu 18.04 and use it to provision Dockerized DigitalOcean Droplets. If you don't have a local Ubuntu 18.04 machine, you can follow these instructions on any Ubuntu 18.04 server.&lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;To follow this tutorial, you will need the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A local machine or server running Ubuntu 18.04 with Docker installed. See &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-and-use-docker-on-ubuntu-18-04"&gt;How To Install and Use Docker on Ubuntu 18.04&lt;/a&gt; for instructions.&lt;/li&gt;
&lt;li&gt;A DigitalOcean API token. If you don't have one, generate it using &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-use-the-digitalocean-api-v2"&gt;this guide&lt;/a&gt;. When you generate a token, be sure that it has read-write scope. That is the default, so if you do not change any options while generating it, it will have read-write capabilities.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="step-1-—-installing-docker-machine"&gt;Step 1 — Installing Docker Machine&lt;/h2&gt;

&lt;p&gt;In order to use Docker Machine, you must first install it locally. On Ubuntu, this means downloading a handful of scripts from the official Docker repository on GitHub.&lt;/p&gt;

&lt;p&gt;To download and install the Docker Machine binary, type:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;wget https://github.com/docker/machine/releases/download/v&lt;span class="highlight"&gt;0.15.0&lt;/span&gt;/docker-machine-$(uname -s)-$(uname -m)
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The name of the file should be &lt;code&gt;docker-machine-Linux-x86_64&lt;/code&gt;. Rename it to &lt;code&gt;docker-machine&lt;/code&gt; to make it easier to work with:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;mv docker-machine-Linux-x86_64 docker-machine
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Make it executable:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;chmod +x docker-machine
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Move or copy it to the &lt;code&gt;/usr/local/bin&lt;/code&gt; directory so that it will be available as a system command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo mv docker-machine /usr/local/bin
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Check the version, which will indicate that it's properly installed:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;docker-machine version
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You'll see output similar to this, displaying the version number and build:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;docker-machine version &lt;span class="highlight"&gt;0.15.0&lt;/span&gt;, build &lt;span class="highlight"&gt;b48dc28d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Docker Machine is installed. Let's install some additional helper tools to make Docker Machine easier to work with.&lt;/p&gt;

&lt;h2 id="step-2-—-installing-additional-docker-machine-scripts"&gt;Step 2 — Installing Additional Docker Machine Scripts&lt;/h2&gt;

&lt;p&gt;There are three Bash scripts in the Docker Machine GitHub repository you can install to make working with the &lt;code&gt;docker&lt;/code&gt; and &lt;code&gt;docker-machine&lt;/code&gt; commands easier. When installed, these scripts provide command completion and prompt customization.&lt;/p&gt;

&lt;p&gt;In this step, you'll install these three scripts into the &lt;code&gt;/etc/bash_completion.d&lt;/code&gt; directory on your local machine by downloading them directly from the Docker Machine GitHub repository.&lt;/p&gt;

&lt;p&gt;&lt;span class='note'&gt;&lt;strong&gt;Note&lt;/strong&gt;: Before downloading and installing a script from the internet in a system-wide location, you should inspect the script's contents first by viewing the source URL in your browser.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;The first script allows you to see the active machine in your prompt. This comes in handy when you are working with and switching between multiple Dockerized machines. The script is called &lt;code&gt;docker-machine-prompt.bash&lt;/code&gt;. Download it &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo wget https://raw.githubusercontent.com/docker/machine/master/contrib/completion/bash/docker-machine-prompt.bash -O /etc/bash_completion.d/docker-machine-prompt.bash
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To complete the installation of this file, you'll have to modify the value for the &lt;code&gt;PS1&lt;/code&gt; variable in your &lt;code&gt;.bashrc&lt;/code&gt; file. The &lt;code&gt;PS1&lt;/code&gt; variable is a special shell variable used to modify the Bash command prompt. Open &lt;code&gt;~/.bashrc&lt;/code&gt; in your editor:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nano ~/.bashrc
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Within that file, there are three lines that begin with &lt;code&gt;PS1&lt;/code&gt;. They should look just like these:&lt;/p&gt;
&lt;div class="code-label " title="~/.bashrc"&gt;~/.bashrc&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '

...

PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ '

...

PS1="\[\e]0;${debian_chroot:+($debian_chroot)}\u@\h: \w\a\]$PS1"
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For each line, insert &lt;code&gt;$(__docker_machine_ps1 " [%s]")&lt;/code&gt; near the end, as shown in the following example:&lt;/p&gt;
&lt;div class="code-label " title="~/.bashrc"&gt;~/.bashrc&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]&lt;span class="highlight"&gt;$(__docker_machine_ps1 " [%s]")&lt;/span&gt;\$ '

...

PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w&lt;span class="highlight"&gt;$(__docker_machine_ps1 " [%s]")&lt;/span&gt;\$ '

...

PS1="\[\e]0;${debian_chroot:+($debian_chroot)}\u@\h: \w\a\]&lt;span class="highlight"&gt;$(__docker_machine_ps1 " [%s]")&lt;/span&gt;$PS1"
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save and close the file.&lt;/p&gt;

&lt;p&gt;The second script is called &lt;code&gt;docker-machine-wrapper.bash&lt;/code&gt;. It adds a &lt;code&gt;use&lt;/code&gt; subcommand to the &lt;code&gt;docker-machine&lt;/code&gt; command, making it significantly easier to switch between Docker hosts. To download it, type:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo wget https://raw.githubusercontent.com/docker/machine/master/contrib/completion/bash/docker-machine-wrapper.bash -O /etc/bash_completion.d/docker-machine-wrapper.bash
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The third script is called &lt;code&gt;docker-machine.bash&lt;/code&gt;. It adds bash completion for &lt;code&gt;docker-machine&lt;/code&gt; commands. Download it using:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo wget https://raw.githubusercontent.com/docker/machine/master/contrib/completion/bash/docker-machine.bash -O /etc/bash_completion.d/docker-machine.bash
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To apply the changes you've made so far, close, then reopen your terminal. If you're logged into the machine via SSH, exit the session and log in again, and you'll have command completion for the &lt;code&gt;docker&lt;/code&gt; and &lt;code&gt;docker-machine&lt;/code&gt; commands. &lt;/p&gt;

&lt;p&gt;Let's test things out by creating a new Docker host with Docker Machine.&lt;/p&gt;

&lt;h2 id="step-3-—-provisioning-a-dockerized-host-using-docker-machine"&gt;Step 3 — Provisioning a Dockerized Host Using Docker Machine&lt;/h2&gt;

&lt;p&gt;Now that you have Docker and Docker Machine running on your local machine, you can provision a Dockerized Droplet on your DigitalOcean account using Docker Machine's &lt;code&gt;docker-machine create&lt;/code&gt; command. If you've not done so already, assign your DigitalOcean API token to an environment variable:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;export &lt;span class="highlight"&gt;DOTOKEN&lt;/span&gt;=&lt;span class="highlight"&gt;your-api-token&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span class='note'&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; This tutorial uses &lt;span class="highlight"&gt;DOTOKEN&lt;/span&gt; as the bash variable for the DO API token. The variable name does not have to be DOTOKEN, and it does not have to be in all caps.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;To make the variable permanent, put it in your &lt;code&gt;~/.bashrc&lt;/code&gt; file. This step is optional, but it is necessary if you want to the value to persist across shell sessions. &lt;/p&gt;

&lt;p&gt;Open that file with &lt;code&gt;nano&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nano ~/.bashrc
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add this line to the file:&lt;/p&gt;
&lt;div class="code-label " title="~/.bashrc"&gt;~/.bashrc&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;export &lt;span class="highlight"&gt;DOTOKEN&lt;/span&gt;=&lt;span class="highlight"&gt;your-api-token&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To activate the variable in the current terminal session, type:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;source ~/.bashrc
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To call the &lt;code&gt;docker-machine create&lt;/code&gt; command successfully you must specify the &lt;em&gt;driver&lt;/em&gt; you wish to use, as well as a machine name. The driver is the adapter for the infrastructure you're going to create. There are drivers for cloud infrastructure providers, as well as drivers for various virtualization platforms.  &lt;/p&gt;

&lt;p&gt;We'll use the &lt;code&gt;digitalocean&lt;/code&gt; driver. Depending on the driver you select, you'll need to provide additional options to create a machine. The &lt;code&gt;digitalocean&lt;/code&gt; driver requires the API token (or the variable that evaluates to it) as its argument, along with the name for the machine you want to create.&lt;/p&gt;

&lt;p&gt;To create your first machine, type this command to create a DigitalOcean Droplet called &lt;code&gt;docker-01&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;docker-machine create --driver digitalocean --digitalocean-access-token $&lt;span class="highlight"&gt;DOTOKEN&lt;/span&gt; &lt;span class="highlight"&gt;docker-01&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You'll see this output as Docker Machine creates the Droplet:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt; ...
Installing Docker...
Copying certs to the local machine directory...
Copying certs to the remote machine...
Setting Docker configuration on the remote daemon...
Checking connection to Docker...
Docker is up and running!
To see how to connect your Docker Client to the Docker Engine running on this virtual machine, run: docker-machine env ubuntu1804-docker
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Docker Machine creates an SSH key pair for the new host so it can access the server remotely. The Droplet is provisioned with an operating system and Docker is installed. When the command is complete, your Docker Droplet is up and running.&lt;/p&gt;

&lt;p&gt;To see the newly-created machine from the command line, type:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;docker-machine ls
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The output will be similar to this, indicating that the new Docker host is running:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;NAME        ACTIVE   DRIVER         STATE     URL                         SWARM   DOCKER        ERRORS
&lt;span class="highlight"&gt;docker-01&lt;/span&gt;   -        digitalocean   &lt;span class="highlight"&gt;Running&lt;/span&gt;   tcp://209.97.155.178:2376           v18.06.1-ce
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now let's look at how to specify the operating system when we create a machine.&lt;/p&gt;

&lt;h2 id="step-4-—-specifying-the-base-os-and-droplet-options-when-creating-a-dockerized-host"&gt;Step 4 — Specifying the Base OS and Droplet Options When Creating a Dockerized Host&lt;/h2&gt;

&lt;p&gt;By default, the base operating system used when creating a Dockerized host with Docker Machine is &lt;em&gt;supposed&lt;/em&gt; to be the latest Ubuntu LTS. However, at the time of this publication, the &lt;code&gt;docker-machine create&lt;/code&gt; command is still using Ubuntu 16.04 LTS as the base operating system, even though Ubuntu 18.04 is the latest LTS edition. So if you need to run Ubuntu 18.04 on a recently-provisioned machine, you'll have to specify Ubuntu along with the desired version by passing the &lt;code&gt;--digitalocean-image&lt;/code&gt; flag to the &lt;code&gt;docker-machine create&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;For example, to create a machine using Ubuntu 18.04, type:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;docker-machine create --driver digitalocean &lt;span class="highlight"&gt;--digitalocean-image ubuntu-18-04-x64&lt;/span&gt; --digitalocean-access-token $&lt;span class="highlight"&gt;DOTOKEN&lt;/span&gt; &lt;span class="highlight"&gt;docker-ubuntu-1804&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You're not limited to a version of Ubuntu. You can create a machine using any operating system supported on DigitalOcean. For example, to create a machine using Debian 8, type:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;docker-machine create --driver digitalocean &lt;span class="highlight"&gt;--digitalocean-image debian-8-x64&lt;/span&gt; --digitalocean-access-token $&lt;span class="highlight"&gt;DOTOKEN&lt;/span&gt; &lt;span class="highlight"&gt;docker-debian&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To provision a Dockerized host using CentOS 7 as the base OS, specify &lt;code&gt;centos-7-0-x86&lt;/code&gt; as the image name, like so:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;docker-machine create --driver digitalocean &lt;span class="highlight"&gt;--digitalocean-image centos-7-0-x64&lt;/span&gt; --digitalocean-access-token $&lt;span class="highlight"&gt;DOTOKEN&lt;/span&gt; &lt;span class="highlight"&gt;docker-centos7&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The base operating system is not the only choice you have. You can also specify the size of the Droplet. By default, it is the smallest Droplet, which has 1 GB of RAM, a single CPU, and a 25 GB SSD. &lt;/p&gt;

&lt;p&gt;Find the size of the Droplet you want to use by looking up the corresponding slug in the &lt;a href="https://developers.digitalocean.com/documentation/v2/#sizes/"&gt;DigitalOcean API documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For example, to provision a machine with 2 GB of RAM, two CPUs, and a 60 GB SSD, use the slug &lt;code&gt;s-2vcpu-2gb&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;docker-machine create --driver digitalocean &lt;span class="highlight"&gt;--digitalocean-size s-2vcpu-2gb&lt;/span&gt; --digitalocean-access-token $&lt;span class="highlight"&gt;DOTOKEN&lt;/span&gt; &lt;span class="highlight"&gt;docker-03&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To see all the flags specific to creating a Docker Machine using the DigitalOcean driver, type:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;docker-machine create --driver digitalocean -h
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span class='tip'&gt;&lt;strong&gt;Tip:&lt;/strong&gt; If you refresh the Droplet page of your DigitalOcean dashboard, you will see the new machines you created using the &lt;code&gt;docker-machine&lt;/code&gt; command.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;Now let's explore some of the other Docker Machine commands.&lt;/p&gt;

&lt;h2 id="step-5-—-executing-additional-docker-machine-commands"&gt;Step 5 — Executing Additional Docker Machine Commands&lt;/h2&gt;

&lt;p&gt;You've seen how to provision a Dockerized host using the &lt;code&gt;create&lt;/code&gt; subcommand, and how to list the hosts available to Docker Machine using the &lt;code&gt;ls&lt;/code&gt; subcommand. In this step, you'll learn a few more useful subcommands.&lt;/p&gt;

&lt;p&gt;To obtain detailed information about a Dockerized host, use the &lt;code&gt;inspect&lt;/code&gt; subcommand, like so:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;docker-machine inspect &lt;span class="highlight"&gt;docker-01&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The output includes lines like the ones in the following output. The &lt;code&gt;Image&lt;/code&gt; line reveals the version of the Linux distribution used and the &lt;code&gt;Size&lt;/code&gt; line indicates the size slug:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;...
{
    "ConfigVersion": 3,
    "Driver": {
        "IPAddress": "203.0.113.71",
        "MachineName": "docker-01",
        "SSHUser": "root",
        "SSHPort": 22,
        ...
        "Image": "&lt;span class="highlight"&gt;ubuntu-16-04-x64&lt;/span&gt;",
        "Size": "&lt;span class="highlight"&gt;s-1vcpu-1gb&lt;/span&gt;",
        ...
    },

---

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To print the connection configuration for a host, type:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;docker-machine config &lt;span class="highlight"&gt;docker-01&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The output will be similar to this:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;--tlsverify
--tlscacert="/home/kamit/.docker/machine/certs/ca.pem"
--tlscert="/home/kamit/.docker/machine/certs/cert.pem"
--tlskey="/home/kamit/.docker/machine/certs/key.pem"
-H=tcp://&lt;span class="highlight"&gt;203.0.113.71&lt;/span&gt;:2376
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The last line in the output of the &lt;code&gt;docker-machine config&lt;/code&gt; command reveals the IP address of the host, but you can also get that piece of information by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;docker-machine ip &lt;span class="highlight"&gt;docker-01&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you need to power down a remote host, you can use &lt;code&gt;docker-machine&lt;/code&gt; to stop it:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;docker-machine stop &lt;span class="highlight"&gt;docker-01&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Verify that it is stopped:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;docker-machine ls
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The output shows that the status of the machine has changed:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Ouput"&gt;Ouput&lt;/div&gt;NAME        ACTIVE   DRIVER         STATE     URL   SWARM   DOCKER    ERRORS
&lt;span class="highlight"&gt;docker-01&lt;/span&gt;   -        digitalocean   &lt;span class="highlight"&gt;Stopped&lt;/span&gt;                 &lt;span class="highlight"&gt;Unknown&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To start it again, use the &lt;code&gt;start&lt;/code&gt; subcommand:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;docker-machine start &lt;span class="highlight"&gt;docker-01&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then review its status again:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;docker-machine ls
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will see that the &lt;code&gt;STATE&lt;/code&gt; is now set &lt;code&gt;Running&lt;/code&gt; for the host:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Ouput"&gt;Ouput&lt;/div&gt;NAME                ACTIVE   DRIVER         STATE     URL                        SWARM   DOCKER    ERRORS

&lt;span class="highlight"&gt;docker-01&lt;/span&gt;           -        digitalocean   &lt;span class="highlight"&gt;Running&lt;/span&gt;   tcp://203.0.113.71:2376            v18.06.1-ce
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next let's look at how to interact with the remote host using SSH.&lt;/p&gt;

&lt;h2 id="step-6-—-executing-commands-on-a-dockerized-host-via-ssh"&gt;Step 6 — Executing Commands on a Dockerized Host via SSH&lt;/h2&gt;

&lt;p&gt;At this point, you've been getting information about your machines, but you can do more than that. For example, you can execute native Linux commands on a Docker host by using the &lt;code&gt;ssh&lt;/code&gt; subcommand of &lt;code&gt;docker-machine&lt;/code&gt; from your local system. This section explains how to perform &lt;code&gt;ssh&lt;/code&gt; commands via &lt;code&gt;docker-machine&lt;/code&gt; as well as how to open an SSH session to a Dockerized host.&lt;/p&gt;

&lt;p&gt;Assuming that you've provisioned a machine with Ubuntu as the operating system, execute the following command from your local system to update the package database on the Docker host:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;docker-machine ssh &lt;span class="highlight"&gt;docker-01&lt;/span&gt; apt-get update
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can even apply available updates using:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;docker-machine ssh &lt;span class="highlight"&gt;docker-01&lt;/span&gt; apt-get upgrade
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Not sure what kernel your remote Docker host is using? Type the following:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;docker-machine ssh &lt;span class="highlight"&gt;docker-01&lt;/span&gt; uname -r
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, you can log in to the remote host with the &lt;code&gt;docker machine ssh&lt;/code&gt; command:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;docker-machine ssh &lt;span class="highlight"&gt;docker-01&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You'll be logged in as the &lt;strong&gt;root&lt;/strong&gt; user and you'll see something similar to the following:&lt;/p&gt;
&lt;pre class="code-pre  second-environment"&gt;&lt;code langs=""&gt;Welcome to Ubuntu 16.04.5 LTS (GNU/Linux 4.4.0-131-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

  Get cloud support with Ubuntu Advantage Cloud Guest:
    http://www.ubuntu.com/business/services/cloud

14 packages can be updated.
10 updates are security updates.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Log out by typing &lt;code&gt;exit&lt;/code&gt; to return to your local machine.&lt;/p&gt;

&lt;p&gt;Next, we'll direct Docker's commands at our remote host.&lt;/p&gt;

&lt;h2 id="step-7-—-activating-a-dockerized-host"&gt;Step 7 — Activating a Dockerized Host&lt;/h2&gt;

&lt;p&gt;Activating a Docker host connects your local Docker client to that system, which makes it possible to run normal &lt;code&gt;docker&lt;/code&gt; commands on the remote system.&lt;/p&gt;

&lt;p&gt;First, use Docker Machine to create a new Docker host called &lt;code&gt;docker-ubuntu&lt;/code&gt; using Ubuntu 18.04:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;docker-machine create --driver digitalocean &lt;span class="highlight"&gt;--digitalocean-image ubuntu-18-04-x64&lt;/span&gt; --digitalocean-access-token $&lt;span class="highlight"&gt;DOTOKEN&lt;/span&gt; &lt;span class="highlight"&gt;docker-ubuntu&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To activate a Docker host, type the following command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;eval $(docker-machine env &lt;span class="highlight"&gt;machine-name&lt;/span&gt;)
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Alternatively, you can activate it by using this command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;docker-machine use &lt;span class="highlight"&gt;machine-name&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span class='tip'&gt;&lt;strong&gt;Tip&lt;/strong&gt; When working with multiple Docker hosts, the &lt;code&gt;docker-machine use&lt;/code&gt; command is the easiest method of switching from one to the other.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;After typing any of these commands, your prompt will change to indicate that your Docker client is pointing to the remote Docker host. It will take this form. The name of the host will be at the end of the prompt:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;span class="highlight"&gt;username&lt;/span&gt;@&lt;span class="highlight"&gt;localmachine&lt;/span&gt;:~ &lt;span class="highlight"&gt;[docker-01]$&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now any &lt;code&gt;docker&lt;/code&gt; command you type at this command prompt will be executed on that remote host.&lt;/p&gt;

&lt;p&gt;Execute &lt;code&gt;docker-machine ls&lt;/code&gt; again:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="[docker-01]$"&gt;docker-machine ls
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You'll see an asterisk under the &lt;code&gt;ACTIVE&lt;/code&gt; column for &lt;code&gt;docker-01&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;NAME                ACTIVE   DRIVER         STATE     URL                         SWARM   DOCKER    ERRORS
docker-01           &lt;span class="highlight"&gt;*&lt;/span&gt;        digitalocean   Running   tcp://203.0.113.71:2376             v18.06.1-ce
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To exit from the remote Docker host, type the following:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="[docker-01]$"&gt;docker-machine use -u
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Your prompt will no longer show the active host.&lt;/p&gt;

&lt;p&gt;Now let's create containers on the remote machine.&lt;/p&gt;

&lt;h2 id="step-8-—-creating-docker-containers-on-a-remote-dockerized-host"&gt;Step 8 — Creating Docker Containers on a Remote Dockerized Host&lt;/h2&gt;

&lt;p&gt;So far, you have provisioned a Dockerized Droplet on your DigitalOcean account and you've activated it — that is, your Docker client is pointing to it. The next logical step is to spin up containers on it. As an example, let's try running the official Nginx container. &lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;docker-machine use&lt;/code&gt; to select your remote machine:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;docker-machine use &lt;span class="highlight"&gt;docker-01&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now execute this command to run an Nginx container on that machine:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="[docker-01]$"&gt;docker run -d -p &lt;span class="highlight"&gt;8080&lt;/span&gt;:&lt;span class="highlight"&gt;80&lt;/span&gt; --name httpserver &lt;span class="highlight"&gt;nginx&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this command, we're mapping port &lt;code&gt;80&lt;/code&gt; in the Nginx container to port &lt;code&gt;8080&lt;/code&gt; on the Dockerized host so that we can access the default Nginx page from anywhere.&lt;/p&gt;

&lt;p&gt;Once the container builds, you will be able to access the default Nginx page by pointing your web browser to &lt;code&gt;http://&lt;span class="highlight"&gt;docker_machine_ip&lt;/span&gt;:8080&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;While the Docker host is still activated (as seen by its name in the prompt), you can list the images on that host:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="[docker-01]$"&gt;docker images
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The output includes the Nginx image you just used:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
&lt;span class="highlight"&gt;nginx&lt;/span&gt;               latest              71c43202b8ac        3 hours ago         109MB
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can also list the active or running containers on the host:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="[docker-01]$"&gt;docker ps
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If the Nginx container you ran in this step is the only active container, the output will look like this:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;CONTAINER ID        IMAGE               COMMAND                  CREATED              STATUS
 PORTS                  NAMES
d3064c237372        &lt;span class="highlight"&gt;nginx&lt;/span&gt;               "nginx -g 'daemon of…"   About a minute ago   Up About a minute
 0.0.0.0:8080-&amp;gt;80/tcp   httpserver
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you intend to create containers on a remote machine, your Docker client must be pointing to it — that is, it must be the active machine in the terminal that you're using. Otherwise you'll be creating the container on your local machine. Again, let your command prompt be your guide.&lt;/p&gt;

&lt;p&gt;Docker Machine can create and manage remote hosts, and it can also remove them.&lt;/p&gt;

&lt;h2 id="step-9-–-removing-docker-hosts"&gt;Step 9 – Removing Docker Hosts&lt;/h2&gt;

&lt;p&gt;You can use Docker Machine to remove a Docker host you've created.  Use the &lt;code&gt;docker-machine rm&lt;/code&gt; command to remove the &lt;code&gt;docker-01&lt;/code&gt; host you created:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;docker-machine rm &lt;span class="highlight"&gt;docker-01&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The Droplet is deleted along with the SSH key created for it. List the hosts again:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;docker-machine ls
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This time, you won't see the &lt;code&gt;docker-01&lt;/code&gt; host listed in the output. And if you've only created one host, you won't see any output at all.&lt;/p&gt;

&lt;p&gt;Be sure to execute the command &lt;code&gt;docker-machine use -u&lt;/code&gt; to point your local Docker daemon back to your local machine.&lt;/p&gt;

&lt;h2 id="step-10-—-disabling-crash-reporting-optional"&gt;Step 10 — Disabling Crash Reporting (Optional)&lt;/h2&gt;

&lt;p&gt;By default, whenever an attempt to provision a Dockerized host using Docker Machine fails, or Docker Machine crashes, some diagnostic information is sent to a Docker account on &lt;a href="https://www.bugsnag.com/"&gt;Bugsnag&lt;/a&gt;. If you're not comfortable with this, you can disable the reporting by creating an empty file called &lt;code&gt;no-error-report&lt;/code&gt; in your local computer's &lt;code&gt;.docker/machine&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;To create the file, type:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;touch ~/.docker/machine/no-error-report
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Check the file for error messages if provisioning fails or Docker Machine crashes.&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;You've installed Docker Machine and used it to provision multiple Docker hosts on DigitalOcean remotely from your local system. From here you should be able to provision as many Dockerized hosts on your DigitalOcean account as you need.&lt;/p&gt;

&lt;p&gt;For more on Docker Machine, visit the &lt;a href="https://docs.docker.com/machine/overview/"&gt;official documentation page&lt;/a&gt;. The three Bash scripts downloaded in this tutorial are hosted on &lt;a href="https://github.com/docker/machine/tree/master/contrib/completion/bash"&gt;this GitHub page&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-back-up-and-restore-a-kubernetes-cluster-on-digitalocean-using-heptio-ark</id>
    <published>2018-09-28T21:21:05Z</published>
    <updated>2018-10-14T00:21:22Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-back-up-and-restore-a-kubernetes-cluster-on-digitalocean-using-heptio-ark"/>
    <title>How To Back Up and Restore a Kubernetes Cluster on DigitalOcean Using Heptio Ark</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://heptio.github.io/ark/"&gt;Heptio Ark&lt;/a&gt; is a convenient backup tool for Kubernetes clusters that compresses and backs up Kubernetes objects to object storage. It also takes snapshots of your cluster's Persistent Volumes using your cloud provider's block storage snapshot features, and can then restore your cluster's objects and Persistent Volumes to a previous state.&lt;/p&gt;

&lt;p&gt;StackPointCloud's &lt;a href="https://github.com/StackPointCloud/ark-plugin-digitalocean"&gt;DigitalOcean Ark Plugin&lt;/a&gt; allows you to use DigitalOcean block storage to snapshot your Persistent Volumes, and Spaces to back up your Kubernetes objects. When running a Kubernetes cluster on DigitalOcean, this allows you to quickly back up your cluster's state and restore it should disaster strike. &lt;/p&gt;

&lt;p&gt;In this tutorial we'll set up and configure the Ark client on a local machine, and deploy the Ark server into our Kubernetes cluster. We'll then deploy a sample Nginx app that uses a Persistent Volume for logging, and simulate a disaster recovery scenario.&lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;Before you begin this tutorial, you should have the following available to you:&lt;/p&gt;

&lt;p&gt;On your local computer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;kubectl&lt;/code&gt; command-line tool, configured to connect to your cluster. You can read more about installing and configuring &lt;code&gt;kubectl&lt;/code&gt; in the &lt;a href="https://kubernetes.io/docs/tasks/tools/install-kubectl/"&gt;official Kubernetes documentation&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://git-scm.com/"&gt;&lt;code&gt;git&lt;/code&gt;&lt;/a&gt; command-line utility. You can learn how to install &lt;code&gt;git&lt;/code&gt; in  &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-contribute-to-open-source-getting-started-with-git"&gt;Getting Started with Git&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In your DigitalOcean account:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;a href="https://www.digitalocean.com/products/kubernetes/"&gt;DigitalOcean Kubernetes&lt;/a&gt; cluster, or a Kubernetes cluster (version &lt;code&gt;1.7.5&lt;/code&gt; or later) on DigitalOcean Droplets&lt;/li&gt;
&lt;li&gt;A DNS server running inside of your cluster. If you are using DigitalOcean Kubernetes, this is running by default. To learn more about configuring a Kubernetes DNS service, consult &lt;a href="https://kubernetes.io/docs/tasks/administer-cluster/dns-custom-nameservers/"&gt;Customizing DNS Service&lt;/a&gt; from the official Kuberentes documentation.&lt;/li&gt;
&lt;li&gt;A DigitalOcean Space that will store your backed-up Kubernetes objects. To learn how to create a Space, consult &lt;a href="https://www.digitalocean.com/docs/spaces/"&gt;the Spaces product documentation&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;An access key pair for your DigitalOcean Space. To learn how to create a set of access keys, consult &lt;a href="https://www.digitalocean.com/docs/spaces/how-to/administrative-access/"&gt;How to Manage Administrative Access to Spaces&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;A personal access token for use with the DigitalOcean API. To learn how to create a personal access token, consult &lt;a href="https://www.digitalocean.com/docs/api/create-personal-access-token/"&gt;How to Create a Personal Access Token&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you have all of this set up, you're ready to begin with this guide.&lt;/p&gt;

&lt;h2 id="step-1-—-installing-the-ark-client"&gt;Step 1 — Installing the Ark Client&lt;/h2&gt;

&lt;p&gt;The Heptio Ark backup tool consists of a client installed on your local computer and a server that runs in your Kubernetes cluster. To begin, we'll install the local Ark client.&lt;/p&gt;

&lt;p&gt;In your web browser, navigate to the Ark GitHub repo &lt;a href="https://github.com/heptio/ark/releases"&gt;releases page&lt;/a&gt;, find the latest release corresponding to your OS and system architecture, and copy the link address. For the purposes of this guide, we'll use an Ubuntu 18.04 server on an x86-64 (or AMD64) processor as our local machine.&lt;/p&gt;

&lt;p&gt;Then, from the command line on your local computer, navigate to the temporary &lt;code&gt;/tmp&lt;/code&gt; directory and &lt;code&gt;cd&lt;/code&gt; into it:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cd /tmp
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Use &lt;code&gt;wget&lt;/code&gt; and the link you copied earlier to download the release tarball:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;wget https://&lt;span class="highlight"&gt;link_copied_from_release_page&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once the download completes, extract the tarball using &lt;code&gt;tar&lt;/code&gt; (note the filename may differ depending on the current release version and your OS):&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;tar -xvzf &lt;span class="highlight"&gt;ark-v0.9.6-linux-amd64.tar.gz&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;/tmp&lt;/code&gt; directory should now contain the extracted &lt;code&gt;ark&lt;/code&gt; binary as well as the tarball you just downloaded.&lt;/p&gt;

&lt;p&gt;Verify that you can run the &lt;code&gt;ark&lt;/code&gt; client by executing the binary:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;./ark --help
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see the following help output:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Heptio Ark is a tool for managing disaster recovery, specifically for Kubernetes
cluster resources. It provides a simple, configurable, and operationally robust
way to back up your application state and associated data.

If you're familiar with kubectl, Ark supports a similar model, allowing you to
execute commands such as 'ark get backup' and 'ark create schedule'. The same
operations can also be performed as 'ark backup get' and 'ark schedule create'.

Usage:
  ark [command]

Available Commands:
  backup      Work with backups
  client      Ark client related commands
  completion  Output shell completion code for the specified shell (bash or zsh)
  create      Create ark resources
  delete      Delete ark resources
  describe    Describe ark resources
  get         Get ark resources
  help        Help about any command
  plugin      Work with plugins
  restic      Work with restic
  restore     Work with restores
  schedule    Work with schedules
  server      Run the ark server
  version     Print the ark version and associated image

. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At this point you should move the &lt;code&gt;ark&lt;/code&gt; executable out of the temporary &lt;code&gt;/tmp&lt;/code&gt; directory and add it to your &lt;code&gt;PATH&lt;/code&gt;. To add it to your &lt;code&gt;PATH&lt;/code&gt; on an Ubuntu system, simply copy it to &lt;code&gt;/usr/local/bin&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo mv ark /usr/local/bin/ark
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You're now ready to configure the Ark server and deploy it to your Kubernetes cluster.&lt;/p&gt;

&lt;h2 id="step-2-—-installing-and-configuring-the-ark-server"&gt;Step 2 — Installing and Configuring the Ark Server&lt;/h2&gt;

&lt;p&gt;Before we deploy Ark into our Kubernetes cluster, we'll first create Ark's prerequisite objects. Ark's prerequisites consist of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;A &lt;code&gt;heptio-ark&lt;/code&gt; Namespace&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;code&gt;ark&lt;/code&gt; Service Account&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Role-based access control (RBAC) rules to grant permissions to the &lt;code&gt;ark&lt;/code&gt; Service Account&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Custom Resources (CRDs) for the Ark-specific resources: &lt;code&gt;Backup&lt;/code&gt;, &lt;code&gt;Schedule&lt;/code&gt;, &lt;code&gt;Restore&lt;/code&gt;, &lt;code&gt;Config&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A YAML file containing the specs for the above Kubernetes objects can be found in the &lt;a href="https://github.com/heptio/ark"&gt;official Ark Git repository&lt;/a&gt;. While still in the &lt;code&gt;/tmp&lt;/code&gt; directory, download the Ark repo using &lt;code&gt;git&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;git clone https://github.com/heptio/ark.git
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once downloaded, navigate into the &lt;code&gt;ark&lt;/code&gt; directory:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cd ark
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The prerequisite resources listed above can be found in the &lt;code&gt;examples/common/00-prereqs.yaml&lt;/code&gt; YAML file. We'll create these resources in our Kubernetes cluster by using &lt;code&gt;kubectl apply&lt;/code&gt; and passing in the file:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl apply -f examples/common/00-prereqs.yaml
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see the following output:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;customresourcedefinition.apiextensions.k8s.io/backups.ark.heptio.com created
customresourcedefinition.apiextensions.k8s.io/schedules.ark.heptio.com created
customresourcedefinition.apiextensions.k8s.io/restores.ark.heptio.com created
customresourcedefinition.apiextensions.k8s.io/configs.ark.heptio.com created
customresourcedefinition.apiextensions.k8s.io/downloadrequests.ark.heptio.com created
customresourcedefinition.apiextensions.k8s.io/deletebackuprequests.ark.heptio.com created
customresourcedefinition.apiextensions.k8s.io/podvolumebackups.ark.heptio.com created
customresourcedefinition.apiextensions.k8s.io/podvolumerestores.ark.heptio.com created
customresourcedefinition.apiextensions.k8s.io/resticrepositories.ark.heptio.com created
customresourcedefinition.apiextensions.k8s.io/backupstoragelocations.ark.heptio.com created
namespace/heptio-ark created
serviceaccount/ark created
clusterrolebinding.rbac.authorization.k8s.io/ark created
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that we've created the necessary Ark Kubernetes objects in our cluster, we can download and install the &lt;a href="https://github.com/StackPointCloud/ark-plugin-digitalocean"&gt;Ark DigitalOcean Plugin&lt;/a&gt;, which will allow us to use DigitalOcean Spaces as a &lt;code&gt;backupStorageProvider&lt;/code&gt; (for Kubernetes objects), and DigitalOcean Block Storage as a &lt;code&gt;persistentVolumeProvider&lt;/code&gt; (for Persistent Volume backups).&lt;/p&gt;

&lt;p&gt;Move back out of the &lt;code&gt;ark&lt;/code&gt; directory and fetch the plugin from StackPointCloud's repo using &lt;code&gt;git&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cd ..
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;git clone https://github.com/StackPointCloud/ark-plugin-digitalocean.git
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Move into the plugin directory:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cd ark-plugin-digitalocean
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We'll now save the access keys for our DigitalOcean Space as a Kubernetes &lt;a href="https://kubernetes.io/docs/concepts/configuration/secret/"&gt;Secret&lt;/a&gt;. First, open up the &lt;code&gt;examples/credentials-ark&lt;/code&gt; file using your favorite editor:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nano examples/credentials-ark
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Replace &lt;code&gt;&amp;lt;AWS_ACCESS_KEY_ID&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;AWS_SECRET_ACCESS_KEY&amp;gt;&lt;/code&gt; with your Spaces access key and secret key:&lt;/p&gt;
&lt;div class="code-label " title="examples/credentials-ark"&gt;examples/credentials-ark&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;[default]
aws_access_key_id=&lt;span class="highlight"&gt;your_spaces_access_key_here&lt;/span&gt;
aws_secret_access_key=&lt;span class="highlight"&gt;your_spaces_secret_key_here&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save and close the file.&lt;/p&gt;

&lt;p&gt;Now, create the &lt;code&gt;cloud-credentials&lt;/code&gt; Secret using &lt;code&gt;kubectl&lt;/code&gt;, inserting your Personal Access Token using the &lt;code&gt;digitalocean_token&lt;/code&gt; data item:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl create secret generic cloud-credentials \
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;    --namespace heptio-ark \
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;    --from-file cloud=examples/credentials-ark \
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;    --from-literal digitalocean_token=&lt;span class="highlight"&gt;your_personal_access_token&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see the following output:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;secret/cloud-credentials created
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To confirm that the &lt;code&gt;cloud-credentials&lt;/code&gt; Secret was created successfully, you can &lt;code&gt;describe&lt;/code&gt; it using &lt;code&gt;kubectl&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl describe secrets/cloud-credentials --namespace heptio-ark
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see the following output describing the &lt;code&gt;cloud-credentials&lt;/code&gt; secret:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Name:         cloud-credentials
Namespace:    heptio-ark
Labels:       &amp;lt;none&amp;gt;
Annotations:  &amp;lt;none&amp;gt;

Type:  Opaque

Data
====
cloud:               115 bytes
digitalocean_token:  64 bytes
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can now move on to creating an Ark &lt;code&gt;Config&lt;/code&gt; object named &lt;code&gt;default&lt;/code&gt;. To do this, we'll edit a YAML configuration file and then create the object in our Kubernetes cluster.&lt;/p&gt;

&lt;p&gt;Open &lt;code&gt;examples/10-ark-config.yaml&lt;/code&gt; in your favorite editor:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nano examples/10-ark-config.yaml
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Insert your Space's name and region in the highlighted fields:&lt;/p&gt;
&lt;div class="code-label " title="examples/10-ark-config.yaml"&gt;examples/10-ark-config.yaml&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;---
apiVersion: ark.heptio.com/v1
kind: Config
metadata:
  namespace: heptio-ark
  name: default
persistentVolumeProvider:
  name: digitalocean
backupStorageProvider:
  name: aws
  bucket: &lt;span class="highlight"&gt;space_name_here&lt;/span&gt;
  config:
    region: &lt;span class="highlight"&gt;space_region_here&lt;/span&gt;
    s3ForcePathStyle: "true"
    s3Url: https://&lt;span class="highlight"&gt;space_region_here&lt;/span&gt;.digitaloceanspaces.com
backupSyncPeriod: 30m
gcSyncPeriod: 30m
scheduleSyncPeriod: 1m
restoreOnlyMode: false
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;persistentVolumeProvider&lt;/code&gt; sets DigitalOcean Block Storage as the the provider for Persistent Volume backups. These will be Block Storage Volume Snapshots. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;backupStorageProvider&lt;/code&gt; sets DigitalOcean Spaces as the provider for Kubernetes object backups. Ark will create a tarball of all your Kubernetes objects (or some, depending on how you execute it), and upload this tarball to Spaces.&lt;/p&gt;

&lt;p&gt;When you're done, save and close the file.&lt;/p&gt;

&lt;p&gt;Create the object in your cluster using &lt;code&gt;kubectl apply&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl apply -f examples/10-ark-config.yaml
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see the following output:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;config.ark.heptio.com/default created
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At this point, we've finished configuring the Ark server and can create its Kubernetes deployment, found in the &lt;code&gt;examples/20-deployment.yaml&lt;/code&gt; configuration file. Let's take a quick look at this file:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cat examples/20-deployment.yaml
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see the following text:&lt;/p&gt;
&lt;div class="code-label " title="examples/20-deployment.yaml"&gt;examples/20-deployment.yaml&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;---
apiVersion: apps/v1beta1
kind: Deployment
metadata:
  namespace: heptio-ark
  name: ark
spec:
  replicas: 1
  template:
    metadata:
      labels:
        component: ark
      annotations:
        prometheus.io/scrape: "true"
        prometheus.io/port: "8085"
        prometheus.io/path: "/metrics"
    spec:
      restartPolicy: Always
      serviceAccountName: ark
      containers:
        - name: ark
          image: gcr.io/heptio-images/ark:latest
          command:
            - /ark
          args:
            - server
          volumeMounts:
            - name: cloud-credentials
              mountPath: /credentials
            - name: plugins
              mountPath: /plugins
            - name: scratch
              mountPath: /scratch
          env:
            - name: AWS_SHARED_CREDENTIALS_FILE
              value: /credentials/cloud
            - name: ARK_SCRATCH_DIR
              value: /scratch
            - name: DIGITALOCEAN_TOKEN
              valueFrom:
                secretKeyRef:
                  key: digitalocean_token
                  name: cloud-credentials
      volumes:
        - name: cloud-credentials
          secret:
            secretName: cloud-credentials
        - name: plugins
          emptyDir: {}
        - name: scratch
          emptyDir: {}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We observe here that we're creating a Deployment called &lt;code&gt;ark&lt;/code&gt; that consists of a single replica of the &lt;code&gt;gcr.io/heptio-images/ark:latest&lt;/code&gt; container. The Pod is configured using the &lt;code&gt;cloud-credentials&lt;/code&gt; secret we created earlier.&lt;/p&gt;

&lt;p&gt;Create the Deployment using &lt;code&gt;kubectl apply&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl apply -f examples/20-deployment.yaml
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see the following output:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;deployment.apps/ark created
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can double check that the Deployment has been successfully created using &lt;code&gt;kubectl get&lt;/code&gt; on the &lt;code&gt;heptio-ark&lt;/code&gt; Namespace :&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl get deployments --namespace=heptio-ark
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see the following output:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;NAME      DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
ark       1         1         1            0           8m
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The Ark server Pod may not start correctly until you install the Ark DigitalOcean plugin. To install the &lt;code&gt;ark-blockstore-digitalocean&lt;/code&gt; plugin, use the &lt;code&gt;ark&lt;/code&gt; client we installed earlier:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;ark plugin add quay.io/stackpoint/ark-blockstore-digitalocean:latest
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can specify the &lt;code&gt;kubeconfig&lt;/code&gt; to use with the &lt;code&gt;--kubeconfig&lt;/code&gt; flag. If you don't use this flag, &lt;code&gt;ark&lt;/code&gt; will check the &lt;code&gt;KUBECONFIG&lt;/code&gt; environment variable and then fall back to the &lt;code&gt;kubectl&lt;/code&gt; default (&lt;code&gt;~/.kube/config&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;At this point Ark is running and fully configured, and ready to back up and restore your Kubernetes cluster objects and Persistent Volumes to DigitalOcean Spaces and Block Storage.&lt;/p&gt;

&lt;p&gt;In the next section, we'll run a quick test to make sure that the backup and restore functionality works as expected.&lt;/p&gt;

&lt;h2 id="step-3-—-testing-backup-and-restore-procedure"&gt;Step 3 — Testing Backup and Restore Procedure&lt;/h2&gt;

&lt;p&gt;Now that we've successfully installed and configured Ark, we can create a test Nginx Deployment and Persistent Volume, and run through a backup and restore drill to ensure that everything is working properly.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;ark-plugin-digitalocean&lt;/code&gt; repository contains a sample Nginx deployment called &lt;code&gt;nginx-pv.yaml&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Let's take a quick look:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cat examples/nginx-pv.yaml
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see the following text:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: nginx-logs
  namespace: nginx-example
  labels:
    app: nginx
spec:
  storageClassName: do-block-storage
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi

---
apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: nginx-deployment
  namespace: nginx-example
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: nginx
    spec:
      volumes:
        - name: nginx-logs
          persistentVolumeClaim:
           claimName: nginx-logs
      containers:
      - image: nginx:1.7.9
        name: nginx
        ports:
        - containerPort: 80
        volumeMounts:
          - mountPath: "/var/log/nginx"
            name: nginx-logs
            readOnly: false

---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: nginx
  name: my-nginx
  namespace: nginx-example
spec:
  ports:
  - port: 80
    targetPort: 80
  selector:
    app: nginx
  type: LoadBalancer
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this file, we observe specs for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An Nginx Deployment consisting of a single replica of the &lt;code&gt;nginx:1.7.9&lt;/code&gt; container image&lt;/li&gt;
&lt;li&gt;A 5Gi Persistent Volume Claim (called &lt;code&gt;nginx-logs&lt;/code&gt;), using the &lt;code&gt;do-block-storage&lt;/code&gt; StorageClass&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;LoadBalancer&lt;/code&gt; Service that exposes port &lt;code&gt;80&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Create the deployment using &lt;code&gt;kubectl apply&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl apply -f examples/nginx-pv.yml
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see the following output:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;namespace/nginx-example created
persistentvolumeclaim/nginx-logs created
deployment.apps/nginx-deployment created
service/my-nginx created
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Check that the Deployment succeeded:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl get deployments --namespace=nginx-example
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see the following output:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;NAME               DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   1         1         1            1           1h
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once &lt;code&gt;Available&lt;/code&gt; reaches 1, fetch the Nginx load balancer’s external IP using &lt;code&gt;kubectl get&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl get services --namespace=nginx-example
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see both the internal &lt;code&gt;CLUSTER-IP&lt;/code&gt; and &lt;code&gt;EXTERNAL-IP&lt;/code&gt; for the &lt;code&gt;my-nginx&lt;/code&gt; Service:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;NAME       TYPE           CLUSTER-IP     EXTERNAL-IP       PORT(S)        AGE
my-nginx   LoadBalancer   &lt;span class="highlight"&gt;10.32.27.0&lt;/span&gt;      &lt;span class="highlight"&gt;203.0.113.0&lt;/span&gt;   80:30754/TCP   3m
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note the &lt;code&gt;EXTERNAL-IP&lt;/code&gt; and navigate to it using your web browser.&lt;/p&gt;

&lt;p&gt;You should see the following NGINX welcome page:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/kubernetes_backup/nginx_welcome.png" alt="Nginx Welcome Page"&gt;&lt;/p&gt;

&lt;p&gt;This indicates that your Nginx Deployment and Service are up and running.&lt;/p&gt;

&lt;p&gt;Before we simulate our disaster scenario, let’s first check the Nginx access logs (stored on a Persistent Volume attached to the Nginx Pod):&lt;/p&gt;

&lt;p&gt;Fetch the Pod’s name using &lt;code&gt;kubectl get&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl get pods --namespace nginx-example
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;NAME                                READY     STATUS    RESTARTS   AGE
nginx-deployment-&lt;span class="highlight"&gt;77d8f78fcb-zt4wr&lt;/span&gt;   1/1       Running   0          29m
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, &lt;code&gt;exec&lt;/code&gt; into the running Nginx container to get a shell inside of it:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl exec -it nginx-deployment-&lt;span class="highlight"&gt;77d8f78fcb-zt4wr&lt;/span&gt; --namespace nginx-example -- /bin/bash
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once inside the Nginx container, &lt;code&gt;cat&lt;/code&gt; the Nginx access logs:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix second-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="#"&gt;cat /var/log/nginx/access.log
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see some Nginx access entries:&lt;/p&gt;
&lt;pre class="code-pre  second-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;10.244.17.1 - - [01/Oct/2018:21:47:01 +0000] "GET / HTTP/1.1" 200 612 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/203.0.113.11 Safari/537.36" "-"
10.244.17.1 - - [01/Oct/2018:21:47:01 +0000] "GET /favicon.ico HTTP/1.1" 404 570 "http://203.0.113.0/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/203.0.113.11 Safari/537.36" "-"
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note these down (especially the timestamps), as we will use them to confirm the success of the restore procedure.&lt;/p&gt;

&lt;p&gt;We can now perform the backup procedure to copy all &lt;code&gt;nginx&lt;/code&gt; Kubernetes objects to Spaces and take a Snapshot of the Persistent Volume we created when deploying Nginx.&lt;/p&gt;

&lt;p&gt;We'll create a backup called &lt;code&gt;nginx-backup&lt;/code&gt; using the &lt;code&gt;ark&lt;/code&gt; client:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;ark backup create nginx-backup --selector app=nginx
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;--selector app=nginx&lt;/code&gt; instructs the Ark server to only back up Kubernetes objects with the &lt;code&gt;app=nginx&lt;/code&gt; Label Selector.&lt;/p&gt;

&lt;p&gt;You should see the following output:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Backup request "nginx-backup" submitted successfully.
Run `ark backup describe nginx-backup` for more details.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Running &lt;code&gt;ark backup describe nginx-backup&lt;/code&gt; should provide the following output after a short delay:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Name:         nginx-backup
Namespace:    heptio-ark
Labels:       &amp;lt;none&amp;gt;
Annotations:  &amp;lt;none&amp;gt;

Phase:  &lt;span class="highlight"&gt;Completed&lt;/span&gt;

Namespaces:
  Included:  *
  Excluded:  &amp;lt;none&amp;gt;

Resources:
  Included:        *
  Excluded:        &amp;lt;none&amp;gt;
  Cluster-scoped:  auto

Label selector:  app=nginx

Snapshot PVs:  auto

TTL:  720h0m0s

Hooks:  &amp;lt;none&amp;gt;

Backup Format Version:  1

Started:    2018-09-26 00:14:30 -0400 EDT
Completed:  2018-09-26 00:14:34 -0400 EDT

Expiration:  2018-10-26 00:14:30 -0400 EDT

Validation errors:  &amp;lt;none&amp;gt;

Persistent Volumes:
  pvc-e4862eac-c2d2-11e8-920b-92c754237aeb:
    Snapshot ID:        2eb66366-c2d3-11e8-963b-0a58ac14428b
    Type:               ext4
    Availability Zone:
    IOPS:               &amp;lt;N/A&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This output indicates that  &lt;code&gt;nginx-backup&lt;/code&gt; completed successfully.&lt;/p&gt;

&lt;p&gt;From the DigitalOcean Cloud Control Panel, navigate to the Space containing your Kubernetes backup files.&lt;/p&gt;

&lt;p&gt;You should see a new directory called &lt;code&gt;nginx-backup&lt;/code&gt; containing the Ark backup files.&lt;/p&gt;

&lt;p&gt;Using the left-hand navigation bar, go to &lt;strong&gt;Images&lt;/strong&gt; and then &lt;strong&gt;Snapshots&lt;/strong&gt;. Within &lt;strong&gt;Snapshots&lt;/strong&gt;, navigate to &lt;strong&gt;Volumes&lt;/strong&gt;. You should see a Snapshot corresponding to the PVC listed in the above output.&lt;/p&gt;

&lt;p&gt;We can now test the restore procedure.&lt;/p&gt;

&lt;p&gt;Let's first delete the &lt;code&gt;nginx-example&lt;/code&gt; Namespace. This will delete everything in the Namespace, including the Load Balancer and Persistent Volume:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl delete namespace nginx-example
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Verify that you can no longer access Nginx at the Load Balancer endpoint, and that the &lt;code&gt;nginx-example&lt;/code&gt; Deployment is no longer running:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl get deployments --namespace=nginx-example
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;No resources found.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can now perform the restore procedure, once again using the &lt;code&gt;ark&lt;/code&gt; client:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;ark restore create --from-backup nginx-backup
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we use &lt;code&gt;create&lt;/code&gt; to create an Ark &lt;code&gt;Restore&lt;/code&gt; object from the &lt;code&gt;nginx-backup&lt;/code&gt; object.&lt;/p&gt;

&lt;p&gt;You should see the following output:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;Restore request "nginx-backup-20180926143828" submitted successfully.
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;Run `ark restore describe nginx-backup-20180926143828` for more details.
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Check the status of the restored Deployment:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl get deployments --namespace=nginx-example
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;NAME               DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   1         1         1            1           1m
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Check for the creation of a Persistent Volume:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt; kubectl get pvc --namespace=nginx-example
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;NAME         STATUS    VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS       AGE
nginx-logs   Bound     pvc-e4862eac-c2d2-11e8-920b-92c754237aeb   5Gi        RWO            do-block-storage   3m
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Navigate to the Nginx Service’s external IP once again to confirm that Nginx is up and running.&lt;/p&gt;

&lt;p&gt;Finally, check the logs on the restored Persistent Volume to confirm that the log history has been preserved post-restore.&lt;/p&gt;

&lt;p&gt;To do this, once again fetch the Pod’s name using &lt;code&gt;kubectl get&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl get pods --namespace nginx-example
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;NAME                                READY     STATUS    RESTARTS   AGE
nginx-deployment-&lt;span class="highlight"&gt;77d8f78fcb-zt4wr&lt;/span&gt;   1/1       Running   0          29m
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then &lt;code&gt;exec&lt;/code&gt; into it:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl exec -it nginx-deployment-&lt;span class="highlight"&gt;77d8f78fcb-zt4wr&lt;/span&gt; --namespace nginx-example -- /bin/bash
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once inside the Nginx container, &lt;code&gt;cat&lt;/code&gt; the Nginx access logs:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix second-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="#"&gt;cat /var/log/nginx/access.log
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre  second-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;10.244.17.1 - - [01/Oct/2018:21:47:01 +0000] "GET / HTTP/1.1" 200 612 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/203.0.113.11 Safari/537.36" "-"
10.244.17.1 - - [01/Oct/2018:21:47:01 +0000] "GET /favicon.ico HTTP/1.1" 404 570 "http://203.0.113.0/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/203.0.113.11 Safari/537.36" "-"
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see the same pre-backup access attempts (note the timestamps), confirming that the Persistent Volume restore was successful. Note that there may be additional attempts in the logs if you visited the Nginx landing page after you performed the restore.&lt;/p&gt;

&lt;p&gt;At this point, we've successfully backed up our Kubernetes objects to DigitalOcean Spaces, and our Persistent Volumes using Block Storage Volume Snapshots. We simulated a disaster scenario, and restored service to the test Nginx application.&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;In this guide we installed and configured the Ark Kubernetes backup tool on a DigitalOcean-based Kubernetes cluster. We configured the tool to back up Kubernetes objects to DigitalOcean Spaces, and back up Persistent Volumes using Block Storage Volume Snapshots.&lt;/p&gt;

&lt;p&gt;Ark can also be used to schedule regular backups of your Kubernetes cluster. To do this, you can use the &lt;code&gt;ark schedule&lt;/code&gt; command. It can also be used to migrate resources from one cluster to another. To learn more about these two use cases, consult the &lt;a href="https://heptio.github.io/ark/v0.9.0/use-cases"&gt;official Ark documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To learn more about DigitalOcean Spaces, consult the &lt;a href="https://www.digitalocean.com/docs/spaces/"&gt;official Spaces documentation&lt;/a&gt;. To learn more about Block Storage Volumes, consult the &lt;a href="https://www.digitalocean.com/docs/volumes/"&gt;Block Storage Volume documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This tutorial builds on the README found in StackPointCloud's &lt;code&gt;ark-plugin-digitalocean&lt;/code&gt; &lt;a href="https://github.com/StackPointCloud/ark-plugin-digitalocean"&gt;GitHub repo&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-build-a-neural-network-to-recognize-handwritten-digits-with-tensorflow</id>
    <published>2018-09-17T21:58:42Z</published>
    <updated>2018-10-13T20:37:44Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-build-a-neural-network-to-recognize-handwritten-digits-with-tensorflow"/>
    <title>How To Build a Neural Network to Recognize Handwritten Digits with TensorFlow</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;Neural networks are used as a method of deep learning, one of the many subfields of artificial intelligence. They were first proposed around 70 years ago as an attempt at simulating the way the human brain works, though in a much more simplified form. Individual ‘neurons’ are connected in layers, with weights assigned to determine how the neuron responds when signals are propagated through the network. Previously, neural networks were limited in the number of neurons they were able to simulate, and therefore the complexity of learning they could achieve. But in recent years, due to advancements in hardware development, we have been able to build very deep networks, and train them on enormous datasets to achieve breakthroughs in machine intelligence.&lt;/p&gt;

&lt;p&gt;These breakthroughs have allowed machines to match and exceed the capabilities of humans at performing certain tasks. One such task is object recognition. Though machines have historically been unable to match human vision, recent advances in deep learning have made it possible to build neural networks which can recognize objects, faces, text, and even emotions. &lt;/p&gt;

&lt;p&gt;In this tutorial, you will implement a small subsection of object recognition—digit recognition. Using &lt;a href="https://www.tensorflow.org/"&gt;TensorFlow&lt;/a&gt;, an open-source Python library developed by the Google Brain labs for deep learning research, you will take hand-drawn images of the numbers 0-9 and build and train a neural network to recognize and predict the correct label for the digit displayed.  &lt;/p&gt;

&lt;p&gt;While you won't need prior experience in practical deep learning or TensorFlow to follow along with this tutorial, we'll assume some familiarity with machine learning terms and concepts such as training and testing, features and labels, optimization, and evaluation. You can learn more about these concepts in &lt;a href="https://www.digitalocean.com/community/tutorials/an-introduction-to-machine-learning"&gt;An Introduction to Machine Learning&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;To complete this tutorial, you'll need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A local &lt;a href="https://www.digitalocean.com/community/tutorial_series/how-to-install-and-set-up-a-local-programming-environment-for-python-3"&gt;Python 3 development environment&lt;/a&gt;, including &lt;a href="https://pypi.org/project/pip/"&gt;pip&lt;/a&gt;, a tool for installing Python packages, and &lt;a href="https://docs.python.org/3/library/venv.html"&gt;venv&lt;/a&gt;, for creating virtual environments.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="step-1-—-configuring-the-project"&gt;Step 1 — Configuring the Project&lt;/h2&gt;

&lt;p&gt;Before you can develop the recognition program, you'll need to install a few dependencies and create a workspace to hold your files. &lt;/p&gt;

&lt;p&gt;We'll use a Python 3 virtual environment to manage our project's dependencies. Create a new directory for your project and navigate to the new directory:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;mkdir tensorflow-demo
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;cd tensorflow-demo
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Execute the following commands to set up the virtual environment for this tutorial:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;python3 -m venv tensorflow-demo
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;source tensorflow-demo/bin/activate
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, install the libraries you'll use in this tutorial. We'll use specific versions of these libraries by creating a &lt;code&gt;requirements.txt&lt;/code&gt; file in the project directory which specifies the requirement and the version we need. Create the &lt;code&gt;requirements.txt&lt;/code&gt; file:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;touch requirements.txt
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Open the file in your text editor and add the following lines to specify the Image, NumPy, and TensorFlow libraries and their versions:&lt;/p&gt;
&lt;div class="code-label " title="requirements.txt"&gt;requirements.txt&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;image==1.5.20
numpy==1.14.3
tensorflow==1.4.0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save the file and exit the editor. Then install these libraries with the following command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;pip install -r requirements.txt
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With the dependencies installed, we can start working on our project.&lt;/p&gt;

&lt;h2 id="step-2-—-importing-the-mnist-dataset"&gt;Step 2 — Importing the MNIST Dataset&lt;/h2&gt;

&lt;p&gt;The dataset we will be using in this tutorial is called the &lt;a href="http://yann.lecun.com/exdb/mnist/"&gt;MNIST&lt;/a&gt; dataset, and it is a classic in the machine learning community. This dataset is made up of images of handwritten digits, 28x28 pixels in size. Here are some examples of the digits included in the dataset:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/handwriting_tensorflow_python3/wBCHXId.png" alt="Examples of MNIST images"&gt;&lt;/p&gt;

&lt;p&gt;Let's create a Python program to work with this dataset. We will use one file for all of our work in this tutorial. Create a new file called &lt;code&gt;main.py&lt;/code&gt;: &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;touch main.py
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now open this file in your text editor of choice and add this line of code to the file to import the TensorFlow library:&lt;/p&gt;
&lt;div class="code-label " title="main.py"&gt;main.py&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-python"&gt;import tensorflow as tf
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add the following lines of code to your file to import the MNIST dataset and store the image data in the variable &lt;code&gt;mnist&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-label " title="main.py"&gt;main.py&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-python"&gt;from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True) # y labels are oh-encoded
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When reading in the data, we are using &lt;em&gt;one-hot-encoding&lt;/em&gt; to represent the labels (the actual digit drawn, e.g. "3") of the images.  One-hot-encoding uses a vector of binary values to represent numeric or categorical values. As our labels are for the digits 0-9, the vector contains ten values, one for each possible digit. One of these values is set to 1, to represent the digit at that index of the vector, and the rest are set to 0. For example, the digit 3 is represented using the vector &lt;code&gt;[0, 0, 0, 1, 0, 0, 0, 0, 0, 0]&lt;/code&gt;. As the value at index 3 is stored as 1, the vector therefore represents the digit 3.  &lt;/p&gt;

&lt;p&gt;To represent the actual images themselves, the 28x28 pixels are flattened into a 1D vector which is 784 pixels in size. Each of the 784 pixels making up the image is stored as a value between 0 and 255. This determines the grayscale of the pixel, as our images are presented in black and white only. So a black pixel is represented by 255, and a white pixel by 0, with the various shades of gray somewhere in between. &lt;/p&gt;

&lt;p&gt;We can use the &lt;code&gt;mnist&lt;/code&gt; variable to find out the size of the dataset we have just imported. Looking at the &lt;code&gt;num_examples&lt;/code&gt; for each of the three subsets, we can determine that the dataset has been split into  55,000 images for training, 5000 for validation, and 10,000 for testing. Add the following lines to your file: &lt;/p&gt;
&lt;div class="code-label " title="main.py"&gt;main.py&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-python"&gt;n_train = mnist.train.num_examples # 55,000
n_validation = mnist.validation.num_examples # 5000
n_test = mnist.test.num_examples # 10,000
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that we have our data imported, it’s time to think about the neural network.&lt;/p&gt;

&lt;h2 id="step-3-—-defining-the-neural-network-architecture"&gt;Step 3 — Defining the Neural Network Architecture&lt;/h2&gt;

&lt;p&gt;The architecture of the neural network refers to elements such as the number of layers in the network, the number of units in each layer, and how the units are connected between layers. As neural networks are loosely inspired by the workings of the human brain, here the term unit is used to represent what we would biologically think of as a neuron. Like neurons passing signals around the brain, units take some values from previous units as input, perform a computation, and then pass on the new value as output to other units. These units are layered to form the network, starting at a minimum with one layer for inputting values, and one layer to output values.  The term &lt;em&gt;hidden layer&lt;/em&gt; is used for all of the layers in between the input and output layers, i.e. those "hidden" from the real world. &lt;/p&gt;

&lt;p&gt;Different architectures can yield drastically different results, as the performance can be thought of as a function of the architecture among other things, such as the parameters, the data, and the duration of training. &lt;/p&gt;

&lt;p&gt;Add the following lines of code to your file to store the number of units per layer in global variables. This allows us to alter the network architecture in one place, and at the end of the tutorial you can test for yourself how different numbers of layers and units will impact the results of our model:&lt;/p&gt;
&lt;div class="code-label " title="main.py"&gt;main.py&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-python"&gt;n_input = 784   # input layer (28x28 pixels)
n_hidden1 = 512 # 1st hidden layer
n_hidden2 = 256 # 2nd hidden layer
n_hidden3 = 128 # 3rd hidden layer
n_output = 10   # output layer (0-9 digits)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The following diagram shows a visualization of the architecture we've designed, with each layer fully connected to the surrounding layers:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/handwriting_tensorflow_python3/cnwitLM.png" alt="Diagram of a neural network"&gt;&lt;/p&gt;

&lt;p&gt;The term "deep neural network" relates to the number of hidden layers, with "shallow" usually meaning just one hidden layer, and "deep" referring to multiple hidden layers. Given enough training data, a shallow neural network with a sufficient number of units should theoretically be able to represent any function that a deep neural network can. But it is often more computationally efficient to use a smaller deep neural network to achieve the same task that would require a shallow network with exponentially more hidden units. Shallow neural networks also often encounter overfitting, where the network essentially memorizes the training data that it has seen, and is not able to generalize the knowledge to new data. This is why deep neural networks are more commonly used: the multiple layers between the raw input data and the output label allow the network to learn features at various levels of abstraction, making the network itself better able to generalize.&lt;/p&gt;

&lt;p&gt;Other elements of the neural network that need to be defined here are the hyperparameters. Unlike the parameters that will get updated during training, these values are set initially and remain constant throughout the process. In your file, set the following variables and values:&lt;/p&gt;
&lt;div class="code-label " title="main.py"&gt;main.py&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-python"&gt;learning_rate = 1e-4
n_iterations = 1000
batch_size = 128
dropout = 0.5
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The learning rate represents ow much the parameters will adjust at each step of the learning process. These adjustments are a key component of training: after each pass through the network we tune the weights slightly to try and reduce the loss.  Larger learning rates can converge faster, but also have the potential to overshoot the optimal values as they are updated. The number of iterations refers to how many times we go through the training step, and the batch size refers to how many training examples we are using at each step. The &lt;code&gt;dropout&lt;/code&gt; variable represents a threshold at which we elimanate some units at random. We will be using &lt;code&gt;dropout&lt;/code&gt; in our final hidden layer to give each unit a 50% chance of being eliminated at every training step. This helps prevent overfitting. &lt;/p&gt;

&lt;p&gt;We have now defined the architecture of our neural network, and the hyperparameters that impact the learning process. The next step is to build the network as a TensorFlow graph. &lt;/p&gt;

&lt;h2 id="step-4-—-building-the-tensorflow-graph"&gt;Step 4 — Building the TensorFlow Graph&lt;/h2&gt;

&lt;p&gt;To build our network, we will set up the network as a computational graph for TensorFlow to execute. The core concept of TensorFlow is the &lt;em&gt;tensor&lt;/em&gt;, a data structure similar to an array or list. initialized, manipulated as they are passed through the graph, and updated through the learning process. &lt;/p&gt;

&lt;p&gt;We’ll start by defining three tensors as &lt;em&gt;placeholders&lt;/em&gt;,  which are tensors that we'll feed values into later. Add the following to your file:&lt;/p&gt;
&lt;div class="code-label " title="main.py"&gt;main.py&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-python"&gt;X = tf.placeholder("float", [None, n_input])
Y = tf.placeholder("float", [None, n_output])
keep_prob = tf.placeholder(tf.float32) 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The only parameter that needs to be specified at its declaration is the size of the data we will be feeding in. For &lt;code&gt;X&lt;/code&gt; we use a shape of &lt;code&gt;[None, 784]&lt;/code&gt;, where &lt;code&gt;None&lt;/code&gt; represents any amount, as we will be feeding in an undefined number of 784-pixel images. The shape of &lt;code&gt;Y&lt;/code&gt; is &lt;code&gt;[None, 10]&lt;/code&gt; as we will be using it for an undefined number of label outputs, with 10 possible classes. The &lt;code&gt;keep_prob&lt;/code&gt; tensor is used to control the dropout rate, and we initialize it as a placeholder rather than an immutable variable because we want to use the same tensor both for training (when &lt;code&gt;dropout&lt;/code&gt; is set to &lt;code&gt;0.5&lt;/code&gt;) and testing (when &lt;code&gt;dropout&lt;/code&gt; is set to &lt;code&gt;1.0&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;The parameters that the network will update in the training process are the &lt;code&gt;weight&lt;/code&gt; and &lt;code&gt;bias&lt;/code&gt; values, so for these we need to set an initial value rather than an empty placeholder. These values are essentially where the network does its learning, as they are used in the activation functions of the neurons, representing the strength of the connections between units. &lt;/p&gt;

&lt;p&gt;Since the values are optimized during training, we could set them to zero for now. But the initial value actually has a significant impact on the final accuracy of the model. We'll use random values from a truncated normal distribution for the weights. We want them to be close to zero, so they can adjust in either a positive or negative direction, and slightly different, so they generate different errors. This will ensure that the model learns something useful. Add these lines:&lt;/p&gt;
&lt;div class="code-label " title="main.py"&gt;main.py&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-python"&gt;weights = {
    'w1': tf.Variable(tf.truncated_normal([n_input, n_hidden1], stddev=0.1)),
    'w2': tf.Variable(tf.truncated_normal([n_hidden1, n_hidden2], stddev=0.1)),
    'w3': tf.Variable(tf.truncated_normal([n_hidden2, n_hidden3], stddev=0.1)),
    'out': tf.Variable(tf.truncated_normal([n_hidden3, n_output], stddev=0.1)),
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For the bias, we use a small constant value to ensure that the tensors activate in the intial stages and therefore contribute to the propagation. The weights and bias tensors are stored in dictionary objects for ease of access. Add this code to your file to define the biases:&lt;/p&gt;
&lt;div class="code-label " title="main.py"&gt;main.py&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-python"&gt;
biases = {
    'b1': tf.Variable(tf.constant(0.1, shape=[n_hidden1])),
    'b2': tf.Variable(tf.constant(0.1, shape=[n_hidden2])),
    'b3': tf.Variable(tf.constant(0.1, shape=[n_hidden3])),
    'out': tf.Variable(tf.constant(0.1, shape=[n_output]))
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, set up the layers of the network by defining the operations that will  manipulate the tensors. Add these lines to your file:&lt;/p&gt;
&lt;div class="code-label " title="main.py"&gt;main.py&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-python"&gt;layer_1 = tf.add(tf.matmul(X, weights['w1']), biases['b1'])
layer_2 = tf.add(tf.matmul(layer_1, weights['w2']), biases['b2'])
layer_3 = tf.add(tf.matmul(layer_2, weights['w3']), biases['b3'])
layer_drop = tf.nn.dropout(layer_3, keep_prob)
output_layer = tf.matmul(layer_3, weights['out']) + biases['out']
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Each hidden layer will execute matrix multiplication on the previous layer’s outputs and the current layer’s weights, and add the bias to these values. At the last hidden layer, we will apply a dropout operation using our &lt;code&gt;keep_prob&lt;/code&gt; value of 0.5.&lt;/p&gt;

&lt;p&gt;The final step in building the graph is to define the loss function that we want to optimize. A popular choice of loss function in TensorFlow programs is &lt;em&gt;cross-entropy&lt;/em&gt;, also known as &lt;em&gt;log-loss&lt;/em&gt;, which quantifies the difference between two probability distributions (the predictions and the labels). A perfect classification would result in a cross-entropy of 0, with the loss completely minimized. &lt;/p&gt;

&lt;p&gt;We also need to choose the optimization algorithm which will be used to minimize the loss function. A process named &lt;em&gt;gradient descent optimization&lt;/em&gt; is a common method for finding the (local) minimum of a function by taking iterative steps along the gradient in a negative (descending) direction. There are several choices of gradient descent optimization algorithms already implemented in TensorFlow, and in this tutorial we will be using the &lt;a href="https://www.tensorflow.org/api_docs/python/tf/train/AdamOptimizer"&gt;Adam optimizer&lt;/a&gt;.  This extends upon gradient descent optimization by using momentum to speed up the process through computing an exponentially weighted average of the gradients and using that in the adjustments. Add the following code to your file:&lt;/p&gt;
&lt;div class="code-label " title="main.py"&gt;main.py&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-python"&gt;cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=Y, logits=output_layer))
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We've now defined the network and built it out with TensorFlow. The next step is to feed data through the graph to train it, and then test that it has actually learnt something. &lt;/p&gt;

&lt;h2 id="step-5-—-training-and-testing"&gt;Step 5 — Training and Testing&lt;/h2&gt;

&lt;p&gt;The training process involves feeding the training dataset through the graph and optimizing the loss function. Every time the network iterates through a batch of more training images, it updates the parameters to reduce the loss in order to more accurately predict the digits shown. The testing process involves running our testing dataset through the trained graph, and keeping track of the number of images that are correctly predicted, so that we can calculate the accuracy.&lt;/p&gt;

&lt;p&gt;Before starting the training process, we will define our method of evaluating the accuracy so we can print it out on mini-batches of data while we train. These printed statements will allow us to check that from the first iteration to the last, loss decreases and accuracy increases; they will also allow us to track whether or not we have ran enough iterations to reach a consistent and optimal result:&lt;/p&gt;
&lt;div class="code-label " title="main.py"&gt;main.py&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-python"&gt;correct_pred = tf.equal(tf.argmax(output_layer, 1), tf.argmax(Y, 1))
accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In &lt;code&gt;correct_pred&lt;/code&gt;, we use the &lt;code&gt;arg_max&lt;/code&gt; function to compare which images are being predicted correctly by looking at the &lt;code&gt;output_layer&lt;/code&gt; (predictions) and &lt;code&gt;Y&lt;/code&gt; (labels), and we use the &lt;code&gt;equal&lt;/code&gt; function to return this as a list of [Booleans](tps://&lt;a href="http://www.digitalocean.com/community/tutorials/understanding-data-types-in-python-3#booleans"&gt;www.digitalocean.com/community/tutorials/understanding-data-types-in-python-3#booleans&lt;/a&gt;). We can then cast this list to floats and calculate the mean to get a total accuracy score. &lt;/p&gt;

&lt;p&gt;We are now ready to initialize a session for running the graph. In this session we will feed the network with our training examples, and once trained, we feed the  same graph with new test examples to determine the accuracy of the model. Add the following lines of code to your file:&lt;/p&gt;
&lt;div class="code-label " title="main.py"&gt;main.py&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-python"&gt;init = tf.global_variables_initializer()
sess = tf.Session()
sess.run(init)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The essence of the training process in deep learning is to optimize the loss function. Here we are aiming to minimize the difference between the predicted labels of the images, and the true labels of the images. The process involves four steps which are repeated for a set number of iterations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Propagate values forward through the network&lt;/li&gt;
&lt;li&gt;Compute the loss&lt;/li&gt;
&lt;li&gt;Propagate values backward through the network&lt;/li&gt;
&lt;li&gt;Update the parameters&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At each training step, the parameters are adjusted slightly to try and reduce the loss for the next step. As the learning progresses, we should see a reduction in loss, and eventually we can stop training and use the network as a model for testing our new data. &lt;/p&gt;

&lt;p&gt;Add this code to the file:&lt;/p&gt;
&lt;div class="code-label " title="main.py"&gt;main.py&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-python"&gt;# train on mini batches
for i in range(n_iterations):
    batch_x, batch_y = mnist.train.next_batch(batch_size)
    sess.run(train_step, feed_dict={X: batch_x, Y: batch_y, keep_prob:dropout})

    # print loss and accuracy (per minibatch)
    if i%100==0:
        minibatch_loss, minibatch_accuracy = sess.run([cross_entropy, accuracy], feed_dict={X: batch_x, Y: batch_y, keep_prob:1.0})
        print("Iteration", str(i), "\t| Loss =", str(minibatch_loss), "\t| Accuracy =", str(minibatch_accuracy))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After 100 iterations of each training step in which we feed a mini-batch of images through the network, we print out the loss and accuracy of that batch. Note that we should not be expecting a decreasing loss and increasing accuracy here, as the values are per batch, not for the entire model. We use mini-batches of images rather than feeding them through individually to speed up the training process and allow the network to see a number of different examples before updating the parameters.&lt;/p&gt;

&lt;p&gt;Once the training is complete, we can run the session on the test images. This time we are using a &lt;code&gt;keep_prob&lt;/code&gt; dropout rate of &lt;code&gt;1.0&lt;/code&gt; to ensure all units are active in the testing process.&lt;/p&gt;

&lt;p&gt;Add this code to the file:&lt;/p&gt;
&lt;div class="code-label " title="main.py"&gt;main.py&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-python"&gt;test_accuracy = sess.run(accuracy, feed_dict={X: mnist.test.images, Y: mnist.test.labels, keep_prob:1.0})
print("\nAccuracy on test set:", test_accuracy)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It’s now time to run our program and see how accurately our neural network can recognize these handwritten digits. Save the &lt;code&gt;main.py&lt;/code&gt; file and execute the following command in the terminal to run the script: &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;python3 main.py
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You'll see an output similar to the following, although individual loss and accuracy results may vary slightly:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Iteration 0     | Loss = 3.67079    | Accuracy = 0.140625
Iteration 100   | Loss = 0.492122   | Accuracy = 0.84375
Iteration 200   | Loss = 0.421595   | Accuracy = 0.882812
Iteration 300   | Loss = 0.307726   | Accuracy = 0.921875
Iteration 400   | Loss = 0.392948   | Accuracy = 0.882812
Iteration 500   | Loss = 0.371461   | Accuracy = 0.90625
Iteration 600   | Loss = 0.378425   | Accuracy = 0.882812
Iteration 700   | Loss = 0.338605   | Accuracy = 0.914062
Iteration 800   | Loss = 0.379697   | Accuracy = 0.875
Iteration 900   | Loss = 0.444303   | Accuracy = 0.90625

Accuracy on test set: 0.9206
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To try and improve the accuracy of our model, or to learn more about the impact of tuning hyperparameters, we can test the effect of changing the learning rate, the dropout threshold, the batch size, and the number of iterations. We can also change the number of units in our hidden layers, and change the amount of hidden layers themselves, to see how different architectures increase or decrease the model accuracy.&lt;/p&gt;

&lt;p&gt;To demonstrate that the network is actually recognizing the hand-drawn images, let's test it on a single image of our own. &lt;/p&gt;

&lt;p&gt;First either download this &lt;a href="https://github.com/do-community/tensorflow-digit-recognition/blob/master/test_img.png?raw=true"&gt;sample test image&lt;/a&gt; or open up a graphics editor and create your own 28x28 pixel image of a digit. &lt;/p&gt;

&lt;p&gt;Open the &lt;code&gt;main.py&lt;/code&gt; file in your editor and add the following lines of code to the top of the file to import two libraries necessary for image manipulation. &lt;/p&gt;
&lt;div class="code-label " title="main.py"&gt;main.py&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-python"&gt;import numpy as np
from PIL import Image
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then at the end of the file, add the following line of code to load the test image of the handwritten digit:&lt;/p&gt;
&lt;div class="code-label " title="main.py"&gt;main.py&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-python"&gt;img = np.invert(Image.open("test_img.png").convert('L')).ravel()

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;open&lt;/code&gt; function of the &lt;code&gt;Image&lt;/code&gt; library loads the test image as a 4D array containing the three RGB color channels and the Alpha transparency. This is not the same representation we used previously when reading in the dataset with TensorFlow, so we'll need to do some extra work to match the format. &lt;/p&gt;

&lt;p&gt;First, we use the &lt;code&gt;convert&lt;/code&gt; function with the &lt;code&gt;L&lt;/code&gt; parameter to reduce the 4D RGBA representation to one grayscale color channel. We store this as a &lt;code&gt;numpy&lt;/code&gt; array and invert it using &lt;code&gt;np.invert&lt;/code&gt;, because the current matrix represents black as 0 and white as 255, whereas we need the opposite. Finally, we call &lt;code&gt;ravel&lt;/code&gt; to flatten the array. &lt;/p&gt;

&lt;p&gt;Now that the image data is structured correctly, we can run a session in the same way as previously, but this time only feeding in the single image for testing. Add the following code to your file to test the image and print the outputted label.&lt;/p&gt;
&lt;div class="code-label " title="main.py"&gt;main.py&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-python"&gt;prediction = sess.run(tf.argmax(output_layer,1), feed_dict={X: [img]})
print ("Prediction for test image:", np.squeeze(prediction))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;np.squeeze&lt;/code&gt; function is called on the prediction to return the single integer from the array (i.e. to go from [2] to 2). The resulting output demonstrates that the network has recognized this image as the digit 2. &lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Prediction for test image: 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can try testing the network with more complex images –– digits that look like other digits, for example, or digits that have been drawn poorly or incorrectly –– to see how well it fares.&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;In this tutorial you successfully trained a neural network to classify the MNIST dataset with around 92% accuracy and tested it on an image of your own. Current state-of-the-art research achieves around 99% on this same problem, using more complex network architectures involving convolutional layers. These use the 2D structure of the image to better represent the contents, unlike our method which flattened all the pixels into one vector of 784 units. You can read more about this topic on the &lt;a href="https://www.tensorflow.org/api_docs/python/tf/nn/convolution"&gt;TensorFlow website&lt;/a&gt;, and see the research papers detailing the most accurate results on the &lt;a href="http://yann.lecun.com/exdb/mnist/"&gt;MNIST website&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now that you know how to build and train a neural network, you can try and use this implementation on your own data, or test it on other popular datasets such as the &lt;a href="http://ufldl.stanford.edu/housenumbers/"&gt;Google StreetView House Numbers&lt;/a&gt;, or the &lt;a href="http://www.cs.utoronto.ca/%7Ekriz/cifar.html"&gt;CIFAR-10&lt;/a&gt; dataset for more general image recognition. &lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-speed-up-wordpress-asset-delivery-using-digitalocean-spaces-cdn</id>
    <published>2018-08-02T21:06:47Z</published>
    <updated>2018-09-27T18:05:59Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-speed-up-wordpress-asset-delivery-using-digitalocean-spaces-cdn"/>
    <title>How to Speed Up WordPress Asset Delivery Using DigitalOcean Spaces CDN</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;Implementing a CDN, or &lt;strong&gt;C&lt;/strong&gt;ontent &lt;strong&gt;D&lt;/strong&gt;elivery &lt;strong&gt;N&lt;/strong&gt;etwork, to deliver your WordPress site's static assets can greatly decrease your servers' bandwidth usage as well as speed up page load times for geographically dispersed users. WordPress static assets include images, CSS stylesheets, and JavaScript files. Leveraging a system of edge servers distributed worldwide, a &lt;a href="https://www.digitalocean.com/community/tutorials/using-a-cdn-to-speed-up-static-content-delivery"&gt;CDN&lt;/a&gt; caches copies of your site's static assets across its network to reduce the distance between end users and this bandwidth-intensive content.&lt;/p&gt;

&lt;p&gt;In a previous Solutions guide, &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-store-wordpress-assets-on-digitalocean-spaces"&gt;How to Store WordPress Assets on DigitalOcean Spaces&lt;/a&gt;, we covered offloading a WordPress site's Media Library (where images and other site content gets stored) to DigitalOcean Spaces, a highly redundant object storage service. We did this using the &lt;a href="https://wordpress.org/plugins/do-spaces-sync/"&gt;DigitalOcean Spaces Sync plugin&lt;/a&gt;, which automatically syncs WordPress uploads to your Space, allowing you to delete these files from your server and free up disk space.&lt;/p&gt;

&lt;p&gt;In this &lt;a href="https://www.digitalocean.com/community/tags/solutions?type=tutorials"&gt;Solutions&lt;/a&gt; guide, we'll extend this procedure by rewriting Media Library asset URLs. This forces users’ browsers to download static assets directly from the DigitalOcean Spaces CDN, a geographically distributed set of cache servers optimized for delivering static content. We’ll go over how to enable the CDN for Spaces, how to rewrite links to serve your WordPress assets from the CDN, and finally how to test that your website’s assets are being correctly delivered by the CDN. &lt;/p&gt;

&lt;p&gt;Additionally, we’ll demonstrate how to implement Media Library offload and link rewriting using two popular paid WordPress plugins: &lt;strong&gt;&lt;a href="https://deliciousbrains.com/wp-offload-s3/"&gt;WP Offload Media&lt;/a&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;a href="https://maxgalleria.com/downloads/media-library-plus-pro/"&gt;Media Library Folders Pro&lt;/a&gt;&lt;/strong&gt;. You should choose the plugin that suits your production needs best.&lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;Before you begin this tutorial, you should have a running WordPress installation on top of a LAMP or LEMP stack. You should also have &lt;a href="http://wp-cli.org/"&gt;WP-CLI&lt;/a&gt; installed on your WordPress server, which you can learn to set up by following &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-back-up-wordpress-site-to-spaces#using-plugins-to-create-backups"&gt;these instructions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To offload your Media Library, you’ll need a DigitalOcean Space and an access key pair:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;To learn how to create a Space, consult &lt;a href="https://www.digitalocean.com/docs/spaces/how-to/create-and-delete"&gt;the Spaces product documentation&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;To learn how to create an access key pair and upload files to your Space using the open source &lt;code&gt;s3cmd&lt;/code&gt; tool, consult &lt;a href="https://www.digitalocean.com/docs/spaces/resources/s3cmd/"&gt;s3cmd 2.x Setup&lt;/a&gt;, also on the DigitalOcean product documentation site.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are a few WordPress plugins that you can use to offload your WordPress assets: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://wordpress.org/plugins/do-spaces-sync/"&gt;DigitalOcean Spaces Sync&lt;/a&gt;&lt;/strong&gt; is a free and open-source WordPress plugin for offloading your Media Library to a DigitalOcean Space. You can learn how to do this in &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-store-wordpress-assets-on-digitalocean-spaces"&gt;How To Store WordPress Assets on DigitalOcean Spaces&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://deliciousbrains.com/wp-offload-media/"&gt;WP Offload Media&lt;/a&gt;&lt;/strong&gt; is a paid plugin that copies files from your WordPress Media Library to DigitalOcean Spaces and rewrites URLs to serve the files from the CDN. With the Assets Pull addon, it can identify assets (CSS, JS, images, etc) used by your site (for example by WordPress themes) and also serve these from CDN.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://maxgalleria.com/downloads/media-library-plus-pro/"&gt;Media Library Folders Pro&lt;/a&gt;&lt;/strong&gt; is another paid plugin that helps you organize your Media Library assets, as well as offload them to DigitalOcean Spaces.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For testing purposes, be sure to have a modern web browser such as  &lt;a href="https://www.google.com/chrome/"&gt;Google Chrome&lt;/a&gt; or &lt;a href="https://www.mozilla.org/firefox"&gt;Firefox&lt;/a&gt; installed on your client (e.g. laptop) computer.&lt;/p&gt;

&lt;p&gt;Once you have a running WordPress installation and have created a DigitalOcean Space, you're ready to enable the CDN for your Space and begin with this guide.&lt;/p&gt;

&lt;h2 id="enabling-spaces-cdn"&gt;Enabling Spaces CDN&lt;/h2&gt;

&lt;p&gt;We’ll begin this guide by enabling the CDN for your DigitalOcean Space. This will not affect the availability of existing objects. With the CDN enabled, objects in your Space will be “pushed out” to edge caches across the content delivery network, and a new CDN endpoint URL will be made available to you. To learn more about how CDNs work, consult &lt;a href="https://www.digitalocean.com/community/tutorials/using-a-cdn-to-speed-up-static-content-delivery"&gt;Using a CDN to Speed Up Static Content Delivery&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;First, enable the CDN for your Space by following &lt;a href="http://digitalocean.com/docs/spaces/how-to/enable-cdn"&gt;How to Enable the Spaces CDN&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Navigate back to your Space and reload the page. You should see a new &lt;strong&gt;Endpoints&lt;/strong&gt; link under your Space name:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/wordpress_cdn_offload/endpoints_link.png" alt="Endpoints Link"&gt;&lt;/p&gt;

&lt;p&gt;These endpoints should contain your Space name. We’re using &lt;code&gt;&lt;span class="highlight"&gt;wordpress-offload&lt;/span&gt;&lt;/code&gt; in this tutorial.&lt;/p&gt;

&lt;p&gt;Notice the addition of the new &lt;strong&gt;Edge&lt;/strong&gt; endpoint. This endpoint routes requests for Spaces objects through the CDN, serving them from the edge cache as much as possible. Note down this &lt;strong&gt;Edge&lt;/strong&gt; endpoint, which you’ll use to configure your WordPress plugin in future steps.&lt;/p&gt;

&lt;p&gt;Now that you have enabled the CDN for your Space, you’re ready to begin configuring your asset offload and link rewriting plugin. &lt;/p&gt;

&lt;p&gt;If you’re using DigitalOcean Spaces Sync and continuing from &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-store-wordpress-assets-on-digitalocean-spaces"&gt;How to Store WordPress Assets on DigitalOcean Spaces&lt;/a&gt;, begin reading from the following section. If you’re not using Spaces Sync, skip to either the &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-speed-up-wordpress-asset-delivery-using-digitalocean-spaces-cdn#wordpress-offload-media-plugin"&gt;WP Offload Media section&lt;/a&gt; or the &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-speed-up-wordpress-asset-delivery-using-digitalocean-spaces-cdn#media-library-folders-pro-and-cdn-enabler-plugins"&gt;Media Library Folders Pro section&lt;/a&gt;, depending on the plugin you choose to use. &lt;/p&gt;

&lt;h2 id="spaces-sync-plugin"&gt;Spaces Sync Plugin&lt;/h2&gt;

&lt;p&gt;If you’d like to use the free and open-source DigitalOcean Spaces Sync and CDN Enabler plugins to serve your files from the CDN's edge caches, follow the steps outlined in this section. &lt;/p&gt;

&lt;p&gt;We’ll begin by ensuring that our WordPress installation and Spaces Sync plugin are configured correctly and are serving assets from DigitalOcean Spaces.&lt;/p&gt;

&lt;h3 id="modifying-spaces-sync-plugin-configuration"&gt;Modifying Spaces Sync Plugin Configuration&lt;/h3&gt;

&lt;p&gt;Continuing from &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-store-wordpress-assets-on-digitalocean-spaces#storing-files-on-spaces"&gt;How To Store WordPress Assets on DigitalOcean Spaces&lt;/a&gt;, your Media Library should be offloaded to your DigitalOcean Space and your Spaces Sync plugin settings should look as follows:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/do_spaces_sync_plugin/sync_cloud_only.png" alt="Sync Cloud Only"&gt;&lt;/p&gt;

&lt;p&gt;We are going to make some minor changes to ensure that our configuration allows us to offload WordPress themes and other directories, beyond the &lt;code&gt;wp-content/uploads&lt;/code&gt; Media Library folder.&lt;/p&gt;

&lt;p&gt;First, we're going to modify the &lt;strong&gt;Full URL-path to files&lt;/strong&gt; field so that the Media Library files are served from our Space’s CDN and not locally from the server. This setting essentially rewrites links to Media Library assets, changing them from file links hosted locally on your WordPress server, to file links hosted on the DigitalOcean Spaces CDN.&lt;/p&gt;

&lt;p&gt;Recall the &lt;strong&gt;Edge&lt;/strong&gt; endpoint you noted down in the &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-speed-up-wordpress-asset-delivery-using-digitalocean-spaces-cdn#enabling-spaces-cdn"&gt;Enabling Spaces CDN&lt;/a&gt; step.&lt;/p&gt;

&lt;p&gt;In this tutorial, the Space's name is &lt;code&gt;&lt;span class="highlight"&gt;wordpress-offload&lt;/span&gt;&lt;/code&gt; and the Space's CDN endpoint is:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;https://&lt;span class="highlight"&gt;wordpress-offload&lt;/span&gt;.&lt;span class="highlight"&gt;nyc3&lt;/span&gt;.cdn.digitaloceanspaces.com
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, in the Spaces Sync plugin settings page, replace the URL in the &lt;strong&gt;Full URL-path to files&lt;/strong&gt; field with your Spaces CDN endpoint, followed by &lt;code&gt;/wp-content/uploads&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;In this tutorial, using the above Spaces CDN endpoint, the full URL would be: &lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;https://&lt;span class="highlight"&gt;wordpress-offload&lt;/span&gt;.&lt;span class="highlight"&gt;nyc3&lt;/span&gt;.cdn.digitaloceanspaces.com/wp-content/uploads
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, for the &lt;strong&gt;Local path&lt;/strong&gt; field, enter the full path to the &lt;code&gt;wp-content/uploads&lt;/code&gt; directory on your WordPress server. In this tutorial, the path to the WordPress installation on the server is &lt;code&gt;/var/www/&lt;span class="highlight"&gt;html&lt;/span&gt;/&lt;/code&gt;, so the full path to &lt;code&gt;uploads&lt;/code&gt; would be &lt;code&gt;/var/www/&lt;span class="highlight"&gt;html&lt;/span&gt;/wp-content/uploads&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;span class='note'&gt;&lt;strong&gt;Note:&lt;/strong&gt; If you’re continuing from &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-store-wordpress-assets-on-digitalocean-spaces"&gt;How To Store WordPress Assets on DigitalOcean Spaces&lt;/a&gt;, this guide will slightly modify the path to files in your Space to enable you to optionally offload themes and other &lt;code&gt;wp-content&lt;/code&gt; assets. You should clear out your Space before doing this, or alternatively you can transfer existing files into the correct &lt;code&gt;wp-content/uploads&lt;/code&gt; Space directory using &lt;a href="https://www.digitalocean.com/docs/spaces/resources/s3cmd-usage/"&gt;s3cmd&lt;/a&gt;.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;In the &lt;strong&gt;Storage prefix&lt;/strong&gt; field, we're going to enter &lt;code&gt;/wp-content/uploads&lt;/code&gt;, which will ensure that we build the correct &lt;code&gt;wp-content&lt;/code&gt; directory hierarchy so that we can offload other WordPress directories to this Space.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Filemask&lt;/strong&gt; can remain wildcarded with &lt;code&gt;*&lt;/code&gt;, unless you'd like to exclude certain files.&lt;/p&gt;

&lt;p&gt;It's not necessary to check the &lt;strong&gt;Store files only in the cloud and delete...&lt;/strong&gt; option; only check this box if you'd like to delete the Media Library assets from your server after they've been successfully uploaded to your DigitalOcean Space.&lt;/p&gt;

&lt;p&gt;Your final settings should look something like this:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/wordpress_cdn_offload/modified_sync_settings.png" alt="Final Spaces Sync Settings"&gt;&lt;/p&gt;

&lt;p&gt;Be sure to replace the above values with the values corresponding to your WordPress installation and Spaces configuration.&lt;/p&gt;

&lt;p&gt;Finally, hit &lt;strong&gt;Save Changes&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You should see a &lt;strong&gt;Settings saved&lt;/strong&gt; box appear at the top of your screen, confirming that the Spaces Sync plugin settings have successfully been updated.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Future&lt;/strong&gt; WordPress Media Library uploads should now be synced to your DigitalOcean Space, and served using the Spaces Content Delivery Network.&lt;/p&gt;

&lt;p&gt;In this step, we did &lt;em&gt;not&lt;/em&gt; offload the WordPress theme or other &lt;code&gt;wp-content&lt;/code&gt; assets. To learn how to transfer these assets to Spaces and serve them using the Spaces CDN, skip to &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-speed-up-wordpress-asset-delivery-using-digitalocean-spaces-cdn#offload-additional-assets-(optional)"&gt;Offload Additional Assets&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To verify and test that your Media Library uploads are being delivered from the Spaces CDN, skip to &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-speed-up-wordpress-asset-delivery-using-digitalocean-spaces-cdn#test-cdn-caching"&gt;Test CDN Caching&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id="wordpress-offload-media-plugin"&gt;WordPress Offload Media Plugin&lt;/h2&gt;

&lt;p&gt;The DeliciousBrains WordPress Offload Media &lt;a href="https://deliciousbrains.com/wp-offload-media/"&gt;plugin&lt;/a&gt; allows you to quickly and automatically upload your Media Library assets to DigitalOcean Spaces and rewrite links to these assets so that you can deliver them directly from Spaces or via the Spaces CDN. In addition, &lt;a href="https://deliciousbrains.com/wp-offload-media/doc/assets-pull-addon/"&gt;the Assets Pull addon&lt;/a&gt; allows you to quickly offload additional WordPress assets like JS, CSS, and font files in combination with a &lt;a href="https://www.digitalocean.com/community/tutorials/using-a-cdn-to-speed-up-static-content-delivery#push-vs-pull-zones"&gt;pull CDN&lt;/a&gt;. Setting up this addon is beyond the scope of this guide but to learn more you can consult &lt;a href="https://deliciousbrains.com/wp-offload-media/doc/assets-pull-addon/"&gt;the DeliciousBrains documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We'll begin by installing and configuring the WP Offload Media plugin for a sample WordPress site.&lt;/p&gt;

&lt;h3 id="installing-wp-offload-media-plugin"&gt;Installing WP Offload Media Plugin&lt;/h3&gt;

&lt;p&gt;To begin, you must purchase a copy of the plugin on the DeliciousBrains &lt;a href="https://deliciousbrains.com/wp-offload-media/pricing/"&gt;plugin site&lt;/a&gt;. Choose the appropriate version depending on the number of assets in your Media Library, and support and feature requirements for your site.&lt;/p&gt;

&lt;p&gt;After going through checkout, you'll be brought to a post-purchase site with a download link for the plugin and a license key. The download link and license key will also be sent to you at the email address you provided when purchasing the plugin.&lt;/p&gt;

&lt;p&gt;Download the plugin and navigate to your WordPress site's admin interface (&lt;code&gt;https://&lt;span class="highlight"&gt;your_site_url&lt;/span&gt;/wp-admin&lt;/code&gt;). Log in if necessary. From here, hover over &lt;strong&gt;Plugins&lt;/strong&gt; and click on &lt;strong&gt;Add New&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;Upload Plugin&lt;/strong&gt; and the top of the page, &lt;strong&gt;Choose File&lt;/strong&gt;, and then select the zip archive you just downloaded.&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;Install Now&lt;/strong&gt;, and then &lt;strong&gt;Activate Plugin&lt;/strong&gt;. You'll be brought to WordPress’s plugin admin interface.&lt;/p&gt;

&lt;p&gt;From here, navigate to the WP Offload Media plugin's settings page by clicking &lt;strong&gt;Settings&lt;/strong&gt; under the plugin name.&lt;/p&gt;

&lt;p&gt;You'll be brought to the following screen:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/wordpress_cdn_offload/wp_offload_storage_provider.png" alt="WP Offload Media Configuration"&gt;&lt;/p&gt;

&lt;p&gt;Click the radio button next to &lt;strong&gt;DigitalOcean Spaces&lt;/strong&gt;. You'll now be prompted to either configure your Spaces Access Key in the &lt;code&gt;wp-config.php&lt;/code&gt; file (recommended), or directly in the web interface (the latter will store your Spaces credentials in the WordPress database). &lt;/p&gt;

&lt;p&gt;We'll configure our Spaces Access Key in &lt;code&gt;wp-config.php&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Log in to your WordPress server via the command line, and navigate to your WordPress root directory (in this tutorial, this is &lt;code&gt;/var/www/html&lt;/code&gt;). From here, open up &lt;code&gt;wp-config.php&lt;/code&gt; in your favorite editor:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano wp-config.php
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Scroll down to the line that says &lt;code&gt;/* That's all, stop editing! Happy blogging. */&lt;/code&gt;, and before it insert the following lines containing your Spaces Access Key pair (to learn how to generate an access key pair, consult the &lt;a href="https://www.digitalocean.com/docs/spaces/how-to/administrative-access/#access-keys"&gt;Spaces product docs&lt;/a&gt;):&lt;/p&gt;
&lt;div class="code-label " title="wp-config.php"&gt;wp-config.php&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . . 
define( 'AS3CF_SETTINGS', serialize( array(
    'provider' =&amp;gt; 'do',
    'access-key-id' =&amp;gt; '&lt;span class="highlight"&gt;your_access_key_here&lt;/span&gt;',
    'secret-access-key' =&amp;gt; '&lt;span class="highlight"&gt;your_secret_key_here&lt;/span&gt;',
) ) );

/* That's all, stop editing! Happy blogging. */
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once you're done editing, save and close the file. The changes will take effect immediately.&lt;/p&gt;

&lt;p&gt;Back in the WordPress Offload Media plugin admin interface, select the radio button next to &lt;strong&gt;Define access keys in wp-config.php&lt;/strong&gt; and hit &lt;strong&gt;Save Changes&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You should be brought to the following interface:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/wordpress_cdn_offload/wp_offload_bucket_selection.png" alt="WP Offload Bucket Selection"&gt;&lt;/p&gt;

&lt;p&gt;On this configuration page, select the appropriate region for your Space using the &lt;strong&gt;Region&lt;/strong&gt; dropdown and enter your Space name next to &lt;strong&gt;Bucket&lt;/strong&gt; (in this tutorial, our Space is called &lt;code&gt;wordpress-offload&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Then, hit &lt;strong&gt;Save Bucket&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You'll be brought to the main WP Offload Media configuration page. At the top you should see the following warning box:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/wordpress_cdn_offload/wp_offload_license.png" alt="WP Offload License"&gt;&lt;/p&gt;

&lt;p&gt;Click on &lt;strong&gt;enter your license key&lt;/strong&gt;, and on the subsequent page enter the license key found in your email receipt or on the checkout page and hit &lt;strong&gt;Activate License&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you entered your license key correctly, you should see &lt;strong&gt;License activated successfully&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Now, navigate back to main WP Offload Media configuration page by clicking on &lt;strong&gt;Media Library&lt;/strong&gt; at the top of the window.&lt;/p&gt;

&lt;p&gt;At this point, WP Offload Media has successfully been configured for use with your DigitalOcean Space. You can now begin offloading assets and delivering them using the Spaces CDN.&lt;/p&gt;

&lt;h3 id="configuring-wp-offload-media"&gt;Configuring WP Offload Media&lt;/h3&gt;

&lt;p&gt;Now that you've linked WP Offload Media with your DigitalOcean Space, you can begin offloading assets and configuring URL rewriting to deliver media from the Spaces CDN.&lt;/p&gt;

&lt;p&gt;You should see the following configuration options on the main WP Offload Media configuration page:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/wordpress_cdn_offload/wp_offload_main_nav.png" alt="WP Offload Main Nav"&gt;&lt;/p&gt;

&lt;p&gt;These defaults should work fine for most use cases. If your Media Library exists at a nonstandard path within your WordPress directory, enter the path in the text box under the &lt;strong&gt;Path&lt;/strong&gt; option.&lt;/p&gt;

&lt;p&gt;If you'd like to change asset URLs so that they are served directly from Spaces and not your WordPress server, ensure the toggle is set to &lt;strong&gt;On&lt;/strong&gt; next to &lt;strong&gt;Rewrite Media URLs&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;To deliver Media Library assets using the Spaces CDN, ensure you've enabled the CDN for your Space (see &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-speed-up-wordpress-asset-delivery-using-digitalocean-spaces-cdn#enabling-spaces-cdn"&gt;Enable Spaces CDN&lt;/a&gt; to learn how) and have noted down the URL for the &lt;strong&gt;Edge&lt;/strong&gt; endpoint. Hit the toggle next to &lt;strong&gt;Custom Domain (CNAME)&lt;/strong&gt;, and In the text box that appears, enter the CDN &lt;strong&gt;Edge&lt;/strong&gt; endpoint URL, without the &lt;code&gt;https://&lt;/code&gt; prefix.&lt;/p&gt;

&lt;p&gt;In this guide the Spaces CDN endpoint is:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;https://&lt;span class="highlight"&gt;wordpress-offload&lt;/span&gt;.nyc3.cdn.digitaloceanspaces.com
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So here we enter:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt; &lt;span class="highlight"&gt;wordpress-offload&lt;/span&gt;.nyc3.cdn.digitaloceanspaces.com
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To improve security, we'll force HTTPS for requests to Media Library assets (now served using the CDN) by setting the toggle to &lt;strong&gt;On&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You can optionally clear out files that have been offloaded to Spaces from your WordPress server to free up disk space. To do this, hit &lt;strong&gt;On&lt;/strong&gt; next to &lt;strong&gt;Remove Files From Server&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Once you've finished configuring WP Offload Media, hit &lt;strong&gt;Save Changes&lt;/strong&gt; at the bottom of the page to save your settings. &lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;URL Preview&lt;/strong&gt; box should display a URL containing your Spaces CDN endpoint. It should look something like the following:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;https://&lt;span class="highlight"&gt;wordpress‑offload&lt;/span&gt;.nyc3.cdn.digitaloceanspaces.com/wp‑content/uploads/2018/09/21211354/photo.jpg&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This URL indicates that WP Offload Media has been successfully configured to deliver Media Library assets using the Spaces CDN. If the path doesn't contain &lt;code&gt;cdn&lt;/code&gt;, ensure that you correctly entered the &lt;strong&gt;Edge&lt;/strong&gt; endpoint URL and &lt;em&gt;not&lt;/em&gt; the &lt;strong&gt;Origin&lt;/strong&gt; URL.&lt;/p&gt;

&lt;p&gt;At this point, WP Offload Media has been set up to deliver your Media Library using Spaces CDN. Any &lt;strong&gt;future&lt;/strong&gt; uploads to your Media Library will be automatically copied over to your DigitalOcean Space and served using the CDN.&lt;/p&gt;

&lt;p&gt;You can now bulk offload existing assets in your Media Library using the built-in upload tool.&lt;/p&gt;

&lt;h3 id="offloading-media-library"&gt;Offloading Media Library&lt;/h3&gt;

&lt;p&gt;We'll use the plugin's built-in "Upload Tool" to offload existing files in our WordPress Media Library.&lt;/p&gt;

&lt;p&gt;On the right-hand side of the main WP Offload Media configuration page, you should see the following box:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/wordpress_cdn_offload/wp_offload_upload_tool.png" alt="WP Offload Upload Tool"&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;Offload Now&lt;/strong&gt; to upload your Media Library files to your DigitalOcean Space.&lt;/p&gt;

&lt;p&gt;If the upload procedure gets interrupted, the box will change to display the following:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/wordpress_cdn_offload/wp_offload_upload_tool_2.png" alt="WP Offload Upload Tool 2"&gt;&lt;/p&gt;

&lt;p&gt;Hit &lt;strong&gt;Offload Remaining Now&lt;/strong&gt; to transfer the remaining files to your DigitalOcean Space.&lt;/p&gt;

&lt;p&gt;Once you've offloaded the remaining items from your Media Library, you should see the following new boxes:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/wordpress_cdn_offload/wp_offload_success.png" alt="WP Offload Success"&gt;&lt;/p&gt;

&lt;p&gt;At this point you've offloaded your Media Library to your Space and are delivering the files to users using the Spaces CDN.&lt;/p&gt;

&lt;p&gt;At any point in time, you can download the files back to your WordPress server from your Space by hitting &lt;strong&gt;Download Files&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;You can also clear out your DigitalOcean Space by hitting &lt;strong&gt;Remove Files&lt;/strong&gt;. Before doing this, ensure that you’ve first downloaded the files back to your WordPress server from Spaces. &lt;/p&gt;

&lt;p&gt;In this step, we learned how to offload our WordPress Media Library to DigitalOcean Spaces and rewrite links to these Library assets using the WP Offload Media plugin. &lt;/p&gt;

&lt;p&gt;To offload additional WordPress assets like themes and JavaScript files, you can use the &lt;a href="https://deliciousbrains.com/wp-offload-media/doc/assets-pull-addon/"&gt;Asset Pull addon&lt;/a&gt; or consult the &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-speed-up-wordpress-asset-delivery-using-digitalocean-spaces-cdn#offload-additional-assets-(optional)"&gt;Offload Additional Assets&lt;/a&gt; section of this guide. &lt;/p&gt;

&lt;p&gt;To verify and test that your Media Library uploads are being delivered from the Spaces CDN, skip to &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-speed-up-wordpress-asset-delivery-using-digitalocean-spaces-cdn#test-cdn-caching"&gt;Test CDN Caching&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id="media-library-folders-pro-and-cdn-enabler-plugins"&gt;Media Library Folders Pro and CDN Enabler Plugins&lt;/h2&gt;

&lt;p&gt;The MaxGalleria Media Library Folders Pro &lt;a href="https://maxgalleria.com/media-library-plus/"&gt;plugin&lt;/a&gt; is a convenient WordPress plugin that allows you to better organize your WordPress Media Library assets. In addition, the free &lt;a href="https://maxgalleria.com/downloads/wordpress-amazon-s3/"&gt;Spaces addon&lt;/a&gt; allows you to bulk offload your Media Library assets to DigitalOcean Spaces, and rewrite URLs to those assets to serve them directly from object storage. You can then enable the Spaces CDN and use the Spaces CDN endpoint to serve your library assets from the distributed delivery network. To accomplish this last step, you can use the &lt;a href="https://wordpress.org/plugins/cdn-enabler/"&gt;CDN Enabler&lt;/a&gt; plugin to rewrite CDN endpoint URLs for your Media Library assets.&lt;/p&gt;

&lt;p&gt;We'll begin by installing and configuring the Media Library Folders Pro (MLFP) plugin, as well as the MLFP Spaces addon. We’ll then install and configure the CDN Enabler plugin to deliver Media Library assets using the Spaces CDN.&lt;/p&gt;

&lt;h3 id="installing-mlfp-plugin"&gt;Installing MLFP Plugin&lt;/h3&gt;

&lt;p&gt;After purchasing the MLFP plugin, you should have received an email containing your MaxGalleria account credentials as well as a plugin download link. Click on the plugin download link to download the MLFP plugin zip archive to your local computer.&lt;/p&gt;

&lt;p&gt;Once you've downloaded the archive, log in to your WordPress site's administration interface (&lt;code&gt;https://&lt;span class="highlight"&gt;your_site_url&lt;/span&gt;/wp-admin&lt;/code&gt;), and navigate to &lt;strong&gt;Plugins&lt;/strong&gt; and then &lt;strong&gt;Add New&lt;/strong&gt; in the left-hand sidebar.&lt;/p&gt;

&lt;p&gt;From the &lt;strong&gt;Add Plugins&lt;/strong&gt; page, click &lt;strong&gt;Upload Plugin&lt;/strong&gt; and then select the zip archive you just downloaded. &lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;Install Now&lt;/strong&gt; to complete the plugin installation, and from the &lt;strong&gt;Installing Plugin&lt;/strong&gt; screen, click &lt;strong&gt;Activate Plugin&lt;/strong&gt; to activate MLFP.&lt;/p&gt;

&lt;p&gt;You should then see a &lt;strong&gt;Media Library Folders Pro&lt;/strong&gt; menu item appear in the left-hand sidebar. Click it to go to the Media Library Folders Pro interface. Covering the plugin's various features is beyond the scope of this guide, but to learn more, you can consult &lt;a href="https://maxgalleria.com/downloads/media-library-plus-pro/"&gt;the MaxGalleria site&lt;/a&gt; and &lt;a href="https://maxgalleria.com/forums/"&gt;forums&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We'll now activate the plugin. Click into &lt;strong&gt;Settings&lt;/strong&gt; under the MLFP menu item, and enter your license key next to the &lt;strong&gt;License Key&lt;/strong&gt; text box. You can find your MLFP license key in the email sent to you when you purchased the plugin. Hit &lt;strong&gt;Save Changes&lt;/strong&gt; and then &lt;strong&gt;Activate License&lt;/strong&gt;. Next, hit &lt;strong&gt;Update Settings&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Your MLFP plugin is now active, and you can use it to organize existing or new Media Library assets for your WordPress site.&lt;/p&gt;

&lt;p&gt;We'll now install and configure the Spaces addon plugin so that you can offload and serve these assets from DigitalOcean Spaces.&lt;/p&gt;

&lt;h3 id="installing-mlfp-spaces-addon-plugin-and-offload-media-library"&gt;Installing MLFP Spaces Addon Plugin and Offload Media Library&lt;/h3&gt;

&lt;p&gt;To install the Spaces Addon, log in to your MaxGalleria &lt;a href="https://maxgalleria.com/my-account/"&gt;account&lt;/a&gt;. You can find your account credentials in an email sent to you when you purchased the MLFP plugin.&lt;/p&gt;

&lt;p&gt;Navigate to the &lt;strong&gt;Addons&lt;/strong&gt; page in the top menu bar and scroll down to &lt;strong&gt;Media Sources&lt;/strong&gt;. From here, click into the &lt;strong&gt;Media Library Folders Pro S3 and Spaces&lt;/strong&gt; option.&lt;/p&gt;

&lt;p&gt;From this page, scroll down to the &lt;strong&gt;Pricing&lt;/strong&gt; section and select the option that suits the size of your WordPress Media Library (for Media Libraries with 3000 images or less, the addon is free).&lt;/p&gt;

&lt;p&gt;After completing the addon "purchase," you can navigate back to your account page (by clicking the &lt;strong&gt;Account&lt;/strong&gt; link in the top menu bar), from which the addon plugin will now be available.&lt;/p&gt;

&lt;p&gt;Click on the &lt;strong&gt;Media Library Folders Pro S3&lt;/strong&gt; image and the plugin download should begin.&lt;/p&gt;

&lt;p&gt;Once the download completes, navigate back to your WordPress administration interface, and install the downloaded plugin using the same method as above, by clicking &lt;strong&gt;Upload Plugin&lt;/strong&gt;. Once again, hit &lt;strong&gt;Activate Plugin&lt;/strong&gt; to activate the plugin.&lt;/p&gt;

&lt;p&gt;You will likely receive a warning about configuring access keys in your &lt;code&gt;wp-config.php&lt;/code&gt; file. We'll configure these now.&lt;/p&gt;

&lt;p&gt;Log in to your WordPress server using the console or SSH, and navigate to your WordPress root directory (in this tutorial, this is &lt;code&gt;/var/www/html&lt;/code&gt;). From here, open up &lt;code&gt;wp-config.php&lt;/code&gt; in your favorite editor:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano wp-config.php
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Scroll down to the line that says &lt;code&gt;/* That's all, stop editing! Happy blogging. */&lt;/code&gt;, and before it insert the following lines containing your Spaces Access Key pair and a plugin configuration option (to learn how to generate an access key pair, consult the &lt;a href="https://www.digitalocean.com/docs/spaces/how-to/administrative-access/#access-keys"&gt;Spaces product docs&lt;/a&gt;):&lt;/p&gt;
&lt;div class="code-label " title="wp-config.php"&gt;wp-config.php&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . . 
define('MF_AWS_ACCESS_KEY_ID', '&lt;span class="highlight"&gt;your_access_key_here&lt;/span&gt;');
define( 'MF_AWS_SECRET_ACCESS_KEY', '&lt;span class="highlight"&gt;your_secret_key_here&lt;/span&gt;');
define('MF_CLOUD_TYPE', 'do')

/* That's all, stop editing! Happy blogging. */
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once you're done editing, save and close the file.&lt;/p&gt;

&lt;p&gt;Now, navigate to your DigitalOcean Space from the &lt;a href="https://cloud.digitalocean.com/"&gt;Cloud Control Panel&lt;/a&gt;, and create a folder called &lt;code&gt;wp-content&lt;/code&gt; by clicking on &lt;strong&gt;New Folder&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;From here, navigate back to the WordPress administration interface, and click into &lt;strong&gt;Media Library Folders Pro&lt;/strong&gt; and then &lt;strong&gt;S3 &amp;amp; Spaces Settings&lt;/strong&gt; in the sidebar. &lt;/p&gt;

&lt;p&gt;The warning banner about configuring access keys should now have disappeared. If it's still present, you should double check your &lt;code&gt;wp-config.php&lt;/code&gt; file for any typos or syntax errors.&lt;/p&gt;

&lt;p&gt;In the &lt;strong&gt;License Key&lt;/strong&gt; text box, enter the license key that was emailed to you after purchasing the Spaces addon. Note that this license key is different from the MLFP license key.  Hit &lt;strong&gt;Save Changes&lt;/strong&gt; and then &lt;strong&gt;Activate License&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Once activated, you should see the following configuration pane:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/wordpress_cdn_offload/spaces_addon_config.png" alt="MLFP Spaces Addon Configuration"&gt;&lt;/p&gt;

&lt;p&gt;From here, click &lt;strong&gt;Select Image Bucket &amp;amp; Region&lt;/strong&gt; to select your DigitalOcean Space. Then select the correct region for your Space and hit &lt;strong&gt;Save Bucket Selection&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You've now successfully connected the Spaces offload plugin to your DigitalOcean Space. You can begin offloading your WordPress Media Library assets.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Use files on the cloud server&lt;/strong&gt; checkbox allows you to specify where Media Library assets will be served from. If you check the box, assets will be served from DigitalOcean Spaces, and URLs to images and other Media Library objects will be correspondingly rewritten. If you plan on using the Spaces CDN to serve your Media Library assets, &lt;strong&gt;do not&lt;/strong&gt; check this box, as the plugin will use the Spaces &lt;strong&gt;Origin&lt;/strong&gt; endpoint and not the CDN &lt;strong&gt;Edge&lt;/strong&gt; endpoint. We will configure CDN link rewriting in a future step.&lt;/p&gt;

&lt;p&gt;Click the &lt;strong&gt;Remove files from local server&lt;/strong&gt; box to delete local Media Library assets once they've been successfully uploaded to DigitalOcean Spaces.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Remove individual downloaded files from the cloud server&lt;/strong&gt; checkbox should be used when bulk downloading files from Spaces to your WordPress server. If checked, these files will be deleted from Spaces after successfully downloading to your WordPress server. We can ignore this option for now.&lt;/p&gt;

&lt;p&gt;Since we're configuring the plugin for use with the Spaces CDN, leave the &lt;strong&gt;Use files on the cloud server&lt;/strong&gt; box unchecked, and hit &lt;strong&gt;Copy Media Library to the cloud server&lt;/strong&gt; to sync your site's WordPress Media Library to your DigitalOcean Space.&lt;/p&gt;

&lt;p&gt;You should see a progress box appear, and then &lt;strong&gt;Upload complete.&lt;/strong&gt; indicating the Media Library sync has concluded successfully.&lt;/p&gt;

&lt;p&gt;Navigate to your DigitalOcean Space to confirm that your Media Library files have been copied to your Space. They should be available in the &lt;code&gt;uploads&lt;/code&gt; subdirectory of the &lt;code&gt;wp-content&lt;/code&gt; directory you created earlier in this step.&lt;/p&gt;

&lt;p&gt;Once your files are available in your Space, you're ready to move on to configuring the Spaces CDN.&lt;/p&gt;

&lt;h3 id="installing-cdn-enabler-plugin-to-deliver-assets-from-spaces-cdn"&gt;Installing CDN Enabler Plugin to Deliver Assets from Spaces CDN&lt;/h3&gt;

&lt;p&gt;To use the Spaces CDN to serve your now offloaded files, first &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-speed-up-wordpress-asset-delivery-using-digitalocean-spaces-cdn#enabling-spaces-cdn"&gt;ensure that you've enabled the CDN&lt;/a&gt; for your Space. &lt;/p&gt;

&lt;p&gt;Once the CDN has been enabled for your Space, you can now install and configure the &lt;a href="https://wordpress.org/plugins/cdn-enabler/"&gt;CDN Enabler&lt;/a&gt; WordPress plugin to rewrite links to your Media Library assets. The plugin will rewrite links to these assets so that they are served from the Spaces CDN endpoint.&lt;/p&gt;

&lt;p&gt;To install CDN Enabler, you can either use the &lt;strong&gt;Plugins&lt;/strong&gt; menu from the WordPress administration interface, or install the plugin directly from the command line. We'll demonstrate the latter procedure here.&lt;/p&gt;

&lt;p&gt;First, log in to your WordPress server. Then, navigate to your plugins directory:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cd &lt;span class="highlight"&gt;/var/www/html&lt;/span&gt;/wp-content/plugins
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Be sure to replace the above path with the path to your WordPress installation.&lt;/p&gt;

&lt;p&gt;From the command line, use the &lt;code&gt;wp-cli&lt;/code&gt; interface to install the plugin:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;wp plugin install cdn-enabler
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, activate the plugin:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;wp plugin activate cdn-enabler
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Back in the WordPress Admin Area, under &lt;strong&gt;Settings&lt;/strong&gt;, you should see a new link to &lt;strong&gt;CDN Enabler&lt;/strong&gt; settings. Click into &lt;strong&gt;CDN Enabler&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You should see the following settings screen:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/wordpress_cdn_offload/cdn_enabler_settings.png" alt="CDN Enabler Settings"&gt;&lt;/p&gt;

&lt;p&gt;Modify the displayed fields as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;CDN URL&lt;/strong&gt;: Enter the Spaces &lt;strong&gt;Edge&lt;/strong&gt; endpoint, which you can find from the Spaces Dashboard. In this tutorial, this is &lt;code&gt;https://&lt;span class="highlight"&gt;wordpress-offload&lt;/span&gt;.nyc3.cdn.digitaloceanspaces.com&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Included Directories&lt;/strong&gt;: Enter &lt;code&gt;wp-content/uploads&lt;/code&gt;. We'll learn how to serve other &lt;code&gt;wp-content&lt;/code&gt; directories in the &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-speed-up-wordpress-asset-delivery-using-digitalocean-spaces-cdn#offload-additional-assets"&gt;Offload Additional Assets&lt;/a&gt; section.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Exclusions&lt;/strong&gt;: Leave the default &lt;code&gt;.php&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Relative Path&lt;/strong&gt;: Leave the box checked&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CDN HTTPS&lt;/strong&gt;: Enable it by checking the box&lt;/li&gt;
&lt;li&gt;Leave the remaining two fields blank&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then, hit &lt;strong&gt;Save Changes&lt;/strong&gt; to save these settings and enable them for your WordPress site.&lt;/p&gt;

&lt;p&gt;At this point you've successfully offloaded your WordPress site's Media Library to DigitalOcean Spaces and are serving them to end users using the CDN. &lt;/p&gt;

&lt;p&gt;In this step, we did &lt;em&gt;not&lt;/em&gt; offload the WordPress theme or other &lt;code&gt;wp-content&lt;/code&gt; assets. To learn how to transfer these assets to Spaces and serve them using the Spaces CDN, skip to &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-speed-up-wordpress-asset-delivery-using-digitalocean-spaces-cdn#offload-additional-assets-(optional)"&gt;Offload Additional Assets&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To verify and test that your Media Library uploads are being delivered from the Spaces CDN, skip to &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-speed-up-wordpress-asset-delivery-using-digitalocean-spaces-cdn#test-cdn-caching"&gt;Test CDN Caching&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id="offloading-additional-assets-optional"&gt;Offloading Additional Assets (Optional)&lt;/h2&gt;

&lt;p&gt;In previous sections of this guide, we’ve learned how to offload our site’s WordPress Media Library to Spaces and serve these files using the Spaces CDN. In this section, we’ll cover offloading and serving additional WordPress assets like themes, JavaScript files, and fonts. &lt;/p&gt;

&lt;p&gt;Most of these static assets live inside of the &lt;code&gt;wp-content&lt;/code&gt; directory (which contains &lt;code&gt;wp-themes&lt;/code&gt;). To offload and rewrite URLs for this directory, we’ll use &lt;a href="https://wordpress.org/plugins/cdn-enabler/"&gt;CDN Enabler&lt;/a&gt;, an open-source plugin developed by KeyCDN.&lt;/p&gt;

&lt;p&gt;If you’re using the &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-speed-up-wordpress-asset-delivery-using-digitalocean-spaces-cdn#wordpress-offload-media-plugin"&gt;WP Offload Media plugin&lt;/a&gt;, you can use the &lt;a href="https://deliciousbrains.com/wp-offload-media/doc/assets-pull-addon/"&gt;Asset Pull addon&lt;/a&gt; to serve these files using a pull CDN. Installing and configuring this addon is beyond the scope of this guide. To learn more, consult the DeliciousBrains product &lt;a href="https://deliciousbrains.com/wp-offload-media/doc/assets-pull-addon/"&gt;page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;First, we’ll install CDN Enabler. We’ll then copy our WordPress themes over to Spaces, and finally configure CDN Enabler to deliver these using the Spaces CDN.&lt;/p&gt;

&lt;p&gt;If you’ve already installed CDN Enabler in a previous step, skip to Step 2.&lt;/p&gt;

&lt;h3 id="step-1-—-installing-cdn-enabler"&gt;Step 1 — Installing CDN Enabler&lt;/h3&gt;

&lt;p&gt;To install CDN Enabler, log in to your WordPress server. Then, navigate to your plugins directory:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cd &lt;span class="highlight"&gt;/var/www/html&lt;/span&gt;/wp-content/plugins
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Be sure to replace the above path with the path to your WordPress installation.&lt;/p&gt;

&lt;p&gt;From the command line, use the &lt;code&gt;wp-cli&lt;/code&gt; interface to install the plugin:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;wp plugin install cdn-enabler
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, activate the plugin:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;wp plugin activate cdn-enabler
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Back in the WordPress Admin Area, under &lt;strong&gt;Settings&lt;/strong&gt;, you should see a new link to &lt;strong&gt;CDN Enabler&lt;/strong&gt; settings. Click into &lt;strong&gt;CDN Enabler&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You should see the following settings screen:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/wordpress_cdn_offload/cdn_enabler_settings.png" alt="CDN Enabler Settings"&gt;&lt;/p&gt;

&lt;p&gt;At this point you’ve successfully installed CDN Enabler. We’ll now upload our WordPress themes to Spaces.&lt;/p&gt;

&lt;h3 id="step-2-—-uploading-static-wordpress-assets-to-spaces"&gt;Step 2 — Uploading Static WordPress Assets to Spaces&lt;/h3&gt;

&lt;p&gt;In this tutorial, to demonstrate a basic plugin configuration, we're only going to serve &lt;code&gt;wp-content/themes&lt;/code&gt;, the WordPress directory containing WordPress themes' PHP, JavaScript, HTML, and image files. You can optionally extend this process to other WordPress directories, like &lt;code&gt;wp-includes&lt;/code&gt;, and even the entire &lt;code&gt;wp-content&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;The theme used by the WordPress installation in this tutorial is &lt;code&gt;twentyseventeen&lt;/code&gt;, the default theme for a fresh WordPress installation at the time of writing. You can repeat these steps for any other theme or WordPress content.&lt;/p&gt;

&lt;p&gt;First, we'll upload our theme to our DigitalOcean Space using &lt;code&gt;s3cmd&lt;/code&gt;. If you haven't yet configured &lt;code&gt;s3cmd&lt;/code&gt;, consult the DigitalOcean &lt;a href="https://www.digitalocean.com/docs/spaces/resources/s3cmd/"&gt;Spaces Product Documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Navigate to your WordPress installation's &lt;code&gt;wp-content&lt;/code&gt; directory:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cd &lt;span class="highlight"&gt;/var/www/html&lt;/span&gt;/wp-content
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;From here, upload the &lt;code&gt;themes&lt;/code&gt; directory to your DigitalOcean Space using &lt;code&gt;s3cmd&lt;/code&gt;. Note that at this point you can choose to upload only a single theme, but for simplicity and to offload as much content as possible from our server, we will upload all the themes in the &lt;code&gt;themes&lt;/code&gt; directory to our Space.&lt;/p&gt;

&lt;p&gt;We'll use &lt;code&gt;find&lt;/code&gt;  to build a list of non-PHP (therefore cacheable) files, which we'll then pipe to &lt;code&gt;s3cmd&lt;/code&gt; to upload to Spaces. We’ll exclude CSS stylesheets as well in this first command as we need to set the &lt;code&gt;text/css&lt;/code&gt; MIME type when uploading them.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;find themes/ -type f -not \( -name '*.php' -or -name '*.css' \) | xargs -I{} s3cmd put --acl-public {} s3://&lt;span class="highlight"&gt;wordpress-offload&lt;/span&gt;/wp-content/{}
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, we instruct &lt;code&gt;find&lt;/code&gt; to search for files within the &lt;code&gt;themes/&lt;/code&gt; directory, and ignore &lt;code&gt;.php&lt;/code&gt; and &lt;code&gt;.css&lt;/code&gt; files. We then use &lt;code&gt;xargs -I{}&lt;/code&gt; to iterate over this list, executing &lt;code&gt;s3cmd put&lt;/code&gt; for each file, and set the file's permissions in Spaces to &lt;code&gt;public&lt;/code&gt; using &lt;code&gt;--acl-public&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Next, we’ll do the same for CSS stylesheets, adding the &lt;code&gt;--mime-type="text/css"&lt;/code&gt; flag to set the &lt;code&gt;text/css&lt;/code&gt; MIME type for the stylesheets on Spaces. This will ensure that Spaces serves your theme's CSS files using the correct &lt;code&gt;Content-Type: text/css&lt;/code&gt; HTTP header:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;find themes/ -type f -name '*.css' | xargs -I{} s3cmd put --acl-public &lt;span class="highlight"&gt;--mime-type="text/css"&lt;/span&gt; {} s3://&lt;span class="highlight"&gt;wordpress-offload&lt;/span&gt;/wp-content/{}
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Again, be sure to replace &lt;code&gt;&lt;span class="highlight"&gt;wordpress-offload&lt;/span&gt;&lt;/code&gt; in the above command with your Space name.&lt;/p&gt;

&lt;p&gt;Now that we’ve uploaded our theme, let’s verify that it can be found at the correct path in our Space. Navigate to your Space using the &lt;a href="https://cloud.digitalocean.com/spaces"&gt;DigitalOcean Cloud Control Panel&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Enter the &lt;code&gt;wp-content&lt;/code&gt; directory, followed by the &lt;code&gt;themes&lt;/code&gt; directory. You should see your theme's directory here. If you don't, verify your &lt;code&gt;s3cmd&lt;/code&gt; configuration and re-upload your theme to your Space.&lt;/p&gt;

&lt;h3 id="step-3-—-configuring-cdn-enabler-to-rewrite-asset-links"&gt;Step 3 — Configuring CDN Enabler to Rewrite Asset Links&lt;/h3&gt;

&lt;p&gt;Now that our theme lives in our Space, and we've set the correct metadata, we can begin serving its files using CDN Enabler and the DigitalOcean Spaces CDN.&lt;/p&gt;

&lt;p&gt;Navigate back to the WordPress Admin Area and click into &lt;strong&gt;Settings&lt;/strong&gt; and then &lt;strong&gt;CDN Enabler&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Here, modify the displayed fields as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;CDN URL&lt;/strong&gt;: Enter the Spaces &lt;strong&gt;Edge&lt;/strong&gt; endpoint, as done in &lt;strong&gt;Step 1&lt;/strong&gt;. In this tutorial, this is &lt;code&gt;https://&lt;span class="highlight"&gt;wordpress-offload&lt;/span&gt;.nyc3.cdn.digitaloceanspaces.com&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Included Directories&lt;/strong&gt;: If you’re &lt;strong&gt;not&lt;/strong&gt; using the MLFP plugin, this should be &lt;code&gt;wp-content/themes&lt;/code&gt;. If you are, this should be &lt;code&gt;wp-content/uploads,wp-content/themes&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Exclusions&lt;/strong&gt;: Leave the default &lt;code&gt;.php&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Relative Path&lt;/strong&gt;: Leave the box checked&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CDN HTTPS&lt;/strong&gt;: Enable it by checking the box&lt;/li&gt;
&lt;li&gt;Leave the remaining two fields blank&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your final settings should look something like this:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/wordpress_cdn_offload/cdn_enabler_final.png" alt="CDN Enabler Final Settings"&gt;&lt;/p&gt;

&lt;p&gt;Hit &lt;strong&gt;Save Changes&lt;/strong&gt; to save these settings and enable them for your WordPress site.&lt;/p&gt;

&lt;p&gt;At this point you've successfully offloaded your WordPress site's theme assets to DigitalOcean Spaces and are serving them to end users using the CDN. We can confirm this using Chrome's DevTools, following the procedure described &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-speed-up-wordpress-asset-delivery-using-digitalocean-spaces-cdn#test-cdn-caching"&gt;below&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Using the CDN Enabler plugin, you can repeat this process for other WordPress directories, like &lt;code&gt;wp-includes&lt;/code&gt;, and even the entire &lt;code&gt;wp-content&lt;/code&gt; directory. &lt;/p&gt;

&lt;h2 id="testing-cdn-caching"&gt;Testing CDN Caching&lt;/h2&gt;

&lt;p&gt;In this section, we’ll demonstrate how to determine where your WordPress assets are being served from (e.g. your host server or the CDN) using Google Chrome’s DevTools.&lt;/p&gt;

&lt;h3 id="step-1-—-adding-sample-image-to-media-library-to-test-syncing"&gt;Step 1 — Adding Sample Image to Media Library to Test Syncing&lt;/h3&gt;

&lt;p&gt;To begin, we’ll first upload a sample image to our Media Library, and verify that it's being served from the DigitalOcean Spaces CDN servers. You can upload an image using the WordPress Admin web interface, or using the &lt;code&gt;wp-cli&lt;/code&gt; command-line tool. In this guide, we’ll use &lt;code&gt;wp-cli&lt;/code&gt; to upload the sample image.&lt;/p&gt;

&lt;p&gt;Log in to your WordPress server using the command line, and navigate to the home directory for the non-root user you've configured. In this tutorial, we’ll use the user &lt;strong&gt;&lt;span class="highlight"&gt;sammy&lt;/span&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cd
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;From here, use &lt;code&gt;curl&lt;/code&gt; to download the DigitalOcean logo to your Droplet (if you already have an image you'd like to test with, skip this step):&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;curl https://assets.digitalocean.com/logos/DO_Logo_horizontal_blue.png &amp;gt; do_logo.png
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, use &lt;code&gt;wp-cli&lt;/code&gt; to import the image to your Media Library:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;wp media import --path=&lt;span class="highlight"&gt;/var/www/html&lt;/span&gt;/ /home/&lt;span class="highlight"&gt;sammy&lt;/span&gt;/do_logo.png
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Be sure to replace &lt;code&gt;&lt;span class="highlight"&gt;/var/www/html&lt;/span&gt;&lt;/code&gt; with the correct path to the directory containing your WordPress files.&lt;/p&gt;

&lt;p&gt;You may see some warnings, but the output should end in the following:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Imported file '/home/sammy/do_logo.png' as attachment ID 10.
Success: Imported 1 of 1 items.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Which indicates that our test image has successfully been copied to the WordPress Media Library, and also uploaded to our DigitalOcean Space, using your preferred offload plugin.&lt;/p&gt;

&lt;p&gt;Navigate to your DigitalOcean Space to confirm:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/wordpress_cdn_offload/spaces_upload_confirm.png" alt="Spaces Upload Success"&gt;&lt;/p&gt;

&lt;p&gt;This indicates that your offload plugin is functioning as expected and automatically syncing WordPress uploads to your DigitalOcean Space. Note that the exact path to your Media Library uploads in the Space will depend on the plugin you’re using to offload your WordPress files.&lt;/p&gt;

&lt;p&gt;Next, we will verify that this file is being served using the Spaces CDN, and not from the server running WordPress.&lt;/p&gt;

&lt;h3 id="step-2-—-inspecting-asset-url"&gt;Step 2 — Inspecting Asset URL&lt;/h3&gt;

&lt;p&gt;From the WordPress admin area (&lt;code&gt;https://&lt;span class="highlight"&gt;your_domain&lt;/span&gt;/wp-admin&lt;/code&gt;), navigate to &lt;strong&gt;Pages&lt;/strong&gt; in the left-hand side navigation menu.&lt;/p&gt;

&lt;p&gt;We will create a sample page containing our uploaded image to determine where it's being served from. You can also run this test by adding the image to an existing page on your WordPress site.&lt;/p&gt;

&lt;p&gt;From the &lt;strong&gt;Pages&lt;/strong&gt; screen, click into &lt;strong&gt;Sample Page&lt;/strong&gt;, or any existing page. You can alternatively create a new page. &lt;/p&gt;

&lt;p&gt;In the page editor, click on &lt;strong&gt;Add Media&lt;/strong&gt;, and select the DigitalOcean logo (or other image you used to test this procedure).&lt;/p&gt;

&lt;p&gt;An &lt;strong&gt;Attachment Details&lt;/strong&gt; pane should appear on the right-hand side of your screen. From this pane, add the image to the page by clicking on &lt;strong&gt;Insert into page&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Now, back in the page editor, click on either &lt;strong&gt;Publish&lt;/strong&gt; (if you created a new sample page) or &lt;strong&gt;Update&lt;/strong&gt; (if you added the image to an existing page) in the &lt;strong&gt;Publish&lt;/strong&gt; box on the right-hand side of your screen.&lt;/p&gt;

&lt;p&gt;Now that the page has successfully been updated to contain the image, navigate to it by clicking on the &lt;strong&gt;Permalink&lt;/strong&gt; under the page title. You'll be brought to this page in your web browser.&lt;/p&gt;

&lt;p&gt;For the purposes of this tutorial, the following steps will assume that you're using Google Chrome, but you can use most modern web browsers to run a similar test.&lt;/p&gt;

&lt;p&gt;From the rendered page preview in your browser, right click on the image and click on &lt;strong&gt;Inspect&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/wordpress_cdn_offload/inspect.png" alt="Inspect Menu"&gt;&lt;/p&gt;

&lt;p&gt;A DevTools window should pop up, highlighting the &lt;code&gt;img&lt;/code&gt; asset in the page's HTML:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/wordpress_cdn_offload/devtools_output.png" alt="DevTools Output"&gt;&lt;/p&gt;

&lt;p&gt;You should see the CDN endpoint for your DigitalOcean Space in this URL (in this tutorial, our Spaces CDN endpoint is &lt;code&gt;https://&lt;span class="highlight"&gt;wordpress-offload.nyc3&lt;/span&gt;.cdn.digitaloceanspaces.com&lt;/code&gt;), indicating that the image asset is being served from the DigitalOcean Spaces CDN edge cache.&lt;/p&gt;

&lt;p&gt;This confirms that your Media Library uploads are being synced to your DigitalOcean Space and served using the Spaces CDN.&lt;/p&gt;

&lt;h3 id="step-3-—-inspecting-asset-response-headers"&gt;Step 3 — Inspecting Asset Response Headers&lt;/h3&gt;

&lt;p&gt;From the DevTools window, we'll run one final test. Click on &lt;strong&gt;Network&lt;/strong&gt; in the toolbar at the top of the window.&lt;/p&gt;

&lt;p&gt;Once in the blank &lt;strong&gt;Network&lt;/strong&gt; window, follow the displayed instructions to reload the page.&lt;/p&gt;

&lt;p&gt;The page assets should populate in the window. Locate your test image in the list of page assets:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/wordpress_cdn_offload/devtools_asset_list.png" alt="Chrome DevTools Asset List"&gt;&lt;/p&gt;

&lt;p&gt;Once you've located your test image, click into it to open an additional information pane. Within this pane, click on &lt;strong&gt;Headers&lt;/strong&gt; to show the response headers for this asset:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/wordpress_cdn_offload/response_headers.png" alt="Response Headers"&gt;&lt;/p&gt;

&lt;p&gt;You should see the &lt;code&gt;Cache-Control&lt;/code&gt; HTTP header, which is a CDN response header. This confirms that this image was served from the Spaces CDN.&lt;/p&gt;

&lt;h3 id="step-4-—-inspecting-urls-for-theme-assets-optional"&gt;Step 4 — Inspecting URLs for Theme Assets (Optional)&lt;/h3&gt;

&lt;p&gt;If you offloaded your &lt;code&gt;wp-themes&lt;/code&gt; (or other) directory as described in &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-speed-up-wordpress-asset-delivery-using-digitalocean-spaces-cdn#offload-additional-assets"&gt;Offload Additional Assets&lt;/a&gt;, you should perform the following brief check to verify that your theme’s assets are being served from the Spaces CDN.&lt;/p&gt;

&lt;p&gt;Navigate to your WordPress site in Google Chrome, and right-click anywhere in the page. In the menu that appears, click on &lt;strong&gt;Inspect&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You'll once again be brought to the Chrome DevTools interface. &lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/wordpress_cdn_offload/chrome_devtools_theme_confirm.png" alt="Chrome DevTools Interface"&gt;&lt;/p&gt;

&lt;p&gt;From here, click into &lt;strong&gt;Sources&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;In the left-hand pane, you should see a list of your WordPress site's assets. Scroll down to your CDN endpoint, and expand the list by clicking the small arrow next to the endpoint name:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/wordpress_cdn_offload/devtools_sources_assets.png" alt="DevTools Site Asset List"&gt;&lt;/p&gt;

&lt;p&gt;Observe that your WordPress theme's header image, JavaScript, and CSS stylesheet are now being served from the Spaces CDN.&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;In this tutorial, we've shown how to offload static content from your WordPress server to DigitalOcean Spaces, and serve this content using the Spaces CDN. In most cases, this should reduce bandwidth on your host infrastructure and speed up page loads for end users, especially those located further away geographically from your WordPress server.&lt;/p&gt;

&lt;p&gt;We demonstrated how to offload and serve both Media Library and &lt;code&gt;themes&lt;/code&gt; assets using the Spaces CDN, but these steps can be extended to further unload the entire &lt;code&gt;wp-content&lt;/code&gt; directory, as well as &lt;code&gt;wp-includes&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Implementing a CDN to deliver static assets is just one way to optimize your WordPress installation. Other plugins like &lt;a href="https://en-ca.wordpress.org/plugins/w3-total-cache/"&gt;W3 Total Cache&lt;/a&gt; can further speed up page loads and improve the SEO of your site. A helpful tool to measure your page load speed and improve it is Google's &lt;a href="https://developers.google.com/speed/pagespeed/insights/"&gt;PageSpeed Insights&lt;/a&gt;. Another helpful tool that provides a waterfall breakdown of request and response times as well as suggested optimizations is &lt;a href="https://www.pingdom.com/"&gt;Pingdom&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To learn more about Content Delivery Networks and how they work, consult &lt;a href="https://www.digitalocean.com/community/tutorials/using-a-cdn-to-speed-up-static-content-delivery"&gt;Using a CDN to Speed Up Static Content Delivery&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-manage-sql-database-cheat-sheet</id>
    <published>2018-09-26T14:34:52Z</published>
    <updated>2018-10-14T00:22:17Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-manage-sql-database-cheat-sheet"/>
    <title>How To Manage an SQL Database</title>
    <content type="html">&lt;h1 id="an-sql-cheat-sheet"&gt;An SQL Cheat Sheet&lt;/h1&gt;

&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;SQL databases come installed with all the commands you need to add, modify, delete, and query your data. This cheat sheet-style guide provides a quick reference to some of the most commonly-used SQL commands.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How to Use This Guide:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This guide is in cheat sheet format with self-contained command-line snippets&lt;/li&gt;
&lt;li&gt;Jump to any section that is relevant to the task you are trying to complete&lt;/li&gt;
&lt;li&gt;When you see &lt;code&gt;&lt;span class="highlight"&gt;highlighted text&lt;/span&gt;&lt;/code&gt; in this guide's commands, keep in mind that this text should refer to the columns, tables, and data in &lt;em&gt;your own&lt;/em&gt; database.&lt;/li&gt;
&lt;li&gt;Throughout this guide, the example data values given are all wrapped in apostrophes (&lt;code&gt;'&lt;/code&gt;). In SQL, it is necessary to wrap any data values that consist of strings in apostrophes. This isn't required for numeric data, but it also won't cause any issues if you do include apostrophes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Please note that, while SQL is recognized as a standard, most SQL database programs have their own proprietary extensions. This guide uses MySQL as the example relational database management system (RDBMS), but the commands given will work with other relational database programs, including PostgreSQL, MariaDB, and SQLite. Where there are significant differences between RDBMSs, we have included the alternative commands.&lt;/p&gt;

&lt;h2 id="opening-up-the-database-prompt-using-socket-trust-authentication"&gt;Opening up the Database Prompt (using Socket/Trust Authentication)&lt;/h2&gt;

&lt;p&gt;By default on Ubuntu 18.04, the &lt;strong&gt;root&lt;/strong&gt; MySQL user can authenticate without a password using the following command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo mysql
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To open up a PostgreSQL prompt, use the following command. This example will log you in as the &lt;strong&gt;postgres&lt;/strong&gt; user, which is the included superuser role, but you can replace that with any already-created role:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo -u &lt;span class="highlight"&gt;postgres&lt;/span&gt; psql
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="opening-up-the-database-prompt-using-password-authentication"&gt;Opening up the Database Prompt (using Password Authentication)&lt;/h2&gt;

&lt;p&gt;If your &lt;strong&gt;root&lt;/strong&gt; MySQL user is set to authenticate with a password, you can do so with the following command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;mysql -u root -p
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you've already set up a non-root user account for your database, you can also use this method to log in as that user:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;mysql -u &lt;span class="highlight"&gt;user&lt;/span&gt; -p
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The above command will prompt you for your password after you run it. If you'd like to supply your password as part of the command, immediately follow the &lt;code&gt;-p&lt;/code&gt; option with your password, with no space between them:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;mysql -u root -p&lt;span class="highlight"&gt;password&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="creating-a-database"&gt;Creating a Database&lt;/h2&gt;

&lt;p&gt;The following command creates a database with default settings.&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="msyql&amp;gt;"&gt;CREATE DATABASE &lt;span class="highlight"&gt;database_name&lt;/span&gt;;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you want your database to use a character set and collation different than the defaults, you can specify those using this syntax:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;CREATE DATABASE &lt;span class="highlight"&gt;database_name&lt;/span&gt; CHARACTER SET &lt;span class="highlight"&gt;character_set&lt;/span&gt; COLLATE &lt;span class="highlight"&gt;collation&lt;/span&gt;;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="listing-databases"&gt;Listing Databases&lt;/h2&gt;

&lt;p&gt;To see what databases exist in your MySQL or MariaDB installation, run the following command:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;SHOW DATABASES;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In PostgreSQL, you can see what databases have been created with the following command:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="postgres=#"&gt;\list
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="deleting-a-database"&gt;Deleting a Database&lt;/h2&gt;

&lt;p&gt;To delete a database, including any tables and data held within it, run a command that follows this structure:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;DROP DATABASE IF EXISTS &lt;span class="highlight"&gt;database&lt;/span&gt;;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="creating-a-user"&gt;Creating a User&lt;/h2&gt;

&lt;p&gt;To create a user profile for your database without specifying any privileges for it, run the following command:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;CREATE USER &lt;span class="highlight"&gt;username&lt;/span&gt; IDENTIFIED BY '&lt;span class="highlight"&gt;password&lt;/span&gt;';
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;PostgreSQL uses a similar, but slightly different, syntax:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="postgres=#"&gt;CREATE USER &lt;span class="highlight"&gt;user&lt;/span&gt; WITH PASSWORD '&lt;span class="highlight"&gt;password&lt;/span&gt;';
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you want to create a new user and grant them privileges in one command, you can do so by issuing a &lt;code&gt;GRANT&lt;/code&gt; statement. The following command creates a new user and grants them full privileges to every database and table in the RDBMS:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;GRANT ALL PRIVILEGES ON *.* TO '&lt;span class="highlight"&gt;username&lt;/span&gt;'@'localhost' IDENTIFIED BY '&lt;span class="highlight"&gt;password&lt;/span&gt;';
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="deleting-a-user"&gt;Deleting a User&lt;/h2&gt;

&lt;p&gt;Use the following syntax to delete a database user profile:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;DROP USER IF EXISTS &lt;span class="highlight"&gt;username&lt;/span&gt;;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that this command will not by default delete any tables created by the deleted user, and attempts to access such tables may result in errors.&lt;/p&gt;

&lt;h2 id="selecting-a-database"&gt;Selecting a Database&lt;/h2&gt;

&lt;p&gt;Before you can create a table, you first have to tell the RDBMS the database in which you'd like to create it. In MySQL and MariaDB, do so with the following syntax:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;USE &lt;span class="highlight"&gt;database&lt;/span&gt;;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In PostgreSQL, you must use the following command to select your desired database:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="postgres=#"&gt;\connect &lt;span class="highlight"&gt;database&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="creating-a-table"&gt;Creating a Table&lt;/h2&gt;

&lt;p&gt;The following command structure creates a new table with the name &lt;code&gt;&lt;span class="highlight"&gt;table&lt;/span&gt;&lt;/code&gt;, and includes two columns, each with their own specific data type:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;CREATE TABLE &lt;span class="highlight"&gt;table&lt;/span&gt; ( &lt;span class="highlight"&gt;column_1&lt;/span&gt; &lt;span class="highlight"&gt;column_1_data_type&lt;/span&gt;, &lt;span class="highlight"&gt;column_2&lt;/span&gt; &lt;span class="highlight"&gt;column_2_data_taype&lt;/span&gt; );
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="deleting-a-table"&gt;Deleting a Table&lt;/h2&gt;

&lt;p&gt;To delete a table entirely, including all its data, run the following:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;DROP TABLE IF EXISTS &lt;span class="highlight"&gt;table&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="inserting-data-into-a-table"&gt;Inserting Data into a Table&lt;/h2&gt;

&lt;p&gt;Use the following syntax to populate a table with one row of data:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;INSERT INTO &lt;span class="highlight"&gt;table&lt;/span&gt; ( &lt;span class="highlight"&gt;column_A&lt;/span&gt;, &lt;span class="highlight"&gt;column_B&lt;/span&gt;, &lt;span class="highlight"&gt;column_C&lt;/span&gt; ) VALUES ( '&lt;span class="highlight"&gt;data_A&lt;/span&gt;', '&lt;span class="highlight"&gt;data_B&lt;/span&gt;', '&lt;span class="highlight"&gt;data_C&lt;/span&gt;' );
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can also populate a table with multiple rows of data using a single command, like this:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;INSERT INTO &lt;span class="highlight"&gt;table&lt;/span&gt; ( &lt;span class="highlight"&gt;column_A&lt;/span&gt;, &lt;span class="highlight"&gt;column_B&lt;/span&gt;, &lt;span class="highlight"&gt;column_C&lt;/span&gt; ) VALUES ( '&lt;span class="highlight"&gt;data_1A&lt;/span&gt;', '&lt;span class="highlight"&gt;data_1B&lt;/span&gt;', '&lt;span class="highlight"&gt;data_1C&lt;/span&gt;' ),  ( '&lt;span class="highlight"&gt;data_2A&lt;/span&gt;', '&lt;span class="highlight"&gt;data_2B&lt;/span&gt;', '&lt;span class="highlight"&gt;data_2C&lt;/span&gt;' ), ( '&lt;span class="highlight"&gt;data_3A&lt;/span&gt;', '&lt;span class="highlight"&gt;data_3B&lt;/span&gt;', '&lt;span class="highlight"&gt;data_3C&lt;/span&gt;' );
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="deleting-data-from-a-table"&gt;Deleting Data from a Table&lt;/h2&gt;

&lt;p&gt;To delete a row of data from a table, use the following command structure. Note that &lt;code&gt;&lt;span class="highlight"&gt;value&lt;/span&gt;&lt;/code&gt; should be the value held in the specified &lt;code&gt;&lt;span class="highlight"&gt;column&lt;/span&gt;&lt;/code&gt; in the row that you want to delete:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;DELETE FROM &lt;span class="highlight"&gt;table&lt;/span&gt; WHERE &lt;span class="highlight"&gt;column&lt;/span&gt;='&lt;span class="highlight"&gt;value&lt;/span&gt;';
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;span class='note'&gt;&lt;p&gt;
&lt;strong&gt;Note:&lt;/strong&gt; If you do not include a &lt;code&gt;WHERE&lt;/code&gt; clause in a &lt;code&gt;DELETE&lt;/code&gt; statement, as in the following example, it will delete all the data held in a table, but not the columns or the table itself:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;DELETE FROM &lt;span class="highlight"&gt;table&lt;/span&gt;;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;/p&gt;&lt;/span&gt;

&lt;h2 id="changing-data-in-a-table"&gt;Changing Data in a Table&lt;/h2&gt;

&lt;p&gt;Use the following syntax to update the data held in a given row. Note that the &lt;code&gt;WHERE&lt;/code&gt; clause at the end of the command tells SQL which row to update. &lt;code&gt;&lt;span class="highlight"&gt;value&lt;/span&gt;&lt;/code&gt; is the value held in &lt;code&gt;&lt;span class="highlight"&gt;column_A&lt;/span&gt;&lt;/code&gt; that aligns with the row you want to change.&lt;/p&gt;

&lt;p&gt;&lt;span class='note'&gt;&lt;strong&gt;Note:&lt;/strong&gt; If you fail to include a &lt;code&gt;WHERE&lt;/code&gt; clause in an &lt;code&gt;UPDATE&lt;/code&gt; statement, the command will replace the data held in every row of the table.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;UPDATE &lt;span class="highlight"&gt;table&lt;/span&gt; SET &lt;span class="highlight"&gt;column_1&lt;/span&gt; = &lt;span class="highlight"&gt;value_1&lt;/span&gt;, &lt;span class="highlight"&gt;column_2&lt;/span&gt; = &lt;span class="highlight"&gt;value_2&lt;/span&gt; WHERE &lt;span class="highlight"&gt;column_A&lt;/span&gt;=&lt;span class="highlight"&gt;value&lt;/span&gt;;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="inserting-a-column"&gt;Inserting a Column&lt;/h2&gt;

&lt;p&gt;The following command syntax will add a new column to a table:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;ALTER TABLE &lt;span class="highlight"&gt;table&lt;/span&gt; ADD COLUMN &lt;span class="highlight"&gt;column&lt;/span&gt; &lt;span class="highlight"&gt;data_type&lt;/span&gt;;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="deleting-a-column"&gt;Deleting a Column&lt;/h2&gt;

&lt;p&gt;A command following this structure will delete a column from a table:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;ALTER TABLE &lt;span class="highlight"&gt;table&lt;/span&gt; DROP COLUMN &lt;span class="highlight"&gt;column&lt;/span&gt;;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="performing-basic-queries"&gt;Performing Basic Queries&lt;/h2&gt;

&lt;p&gt;To view all the data from a single column in a table, use the following syntax:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;SELECT &lt;span class="highlight"&gt;column&lt;/span&gt; FROM &lt;span class="highlight"&gt;table&lt;/span&gt;;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To query multiple columns from the same table, separate the column names with a comma:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;SELECT &lt;span class="highlight"&gt;column_1&lt;/span&gt;, &lt;span class="highlight"&gt;column_2&lt;/span&gt; FROM &lt;span class="highlight"&gt;table&lt;/span&gt;;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can also query every column in a table by replacing the names of the columns with an asterisk (&lt;code&gt;*&lt;/code&gt;). In SQL, asterisks act as placeholders to represent “all”:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;SELECT * FROM &lt;span class="highlight"&gt;table&lt;/span&gt;;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="using-where-clauses"&gt;Using WHERE Clauses&lt;/h2&gt;

&lt;p&gt;You can narrow down the results of a query by appending the &lt;code&gt;SELECT&lt;/code&gt; statement with a &lt;code&gt;WHERE&lt;/code&gt; clause, like this:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;SELECT &lt;span class="highlight"&gt;column&lt;/span&gt; FROM &lt;span class="highlight"&gt;table&lt;/span&gt; WHERE &lt;span class="highlight"&gt;conditions_that_apply&lt;/span&gt;;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For example, you can query all the data from a single row with a syntax like the following. Note that &lt;code&gt;&lt;span class="highlight"&gt;value&lt;/span&gt;&lt;/code&gt; should be a value held in both the specified &lt;code&gt;&lt;span class="highlight"&gt;column&lt;/span&gt;&lt;/code&gt; and the row you want to query:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;SELECT * FROM &lt;span class="highlight"&gt;table&lt;/span&gt; WHERE &lt;span class="highlight"&gt;column&lt;/span&gt; = &lt;span class="highlight"&gt;value&lt;/span&gt;;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="working-with-comparison-operators"&gt;Working with Comparison Operators&lt;/h2&gt;

&lt;p&gt;A comparison operator in a &lt;code&gt;WHERE&lt;/code&gt; clause defines how the specified column should be compared against the value. Here are some common SQL comparison operators:&lt;/p&gt;

&lt;table&gt;&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operator&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;=&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;tests for equality&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;!=&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;tests for inequality&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;tests for less-than&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;tests for greater-than&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;=&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;tests for less-than or equal-to&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;gt;=&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;tests for greater-than or equal-to&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;BETWEEN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;tests whether a value lies within a given range&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;IN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;tests whether a row's value is contained in a set of specified values&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;EXISTS&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;tests whether rows exist, given the specified conditions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;LIKE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;tests whether a value matches a specified string&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;IS NULL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;tests for &lt;code&gt;NULL&lt;/code&gt; values&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;IS NOT NULL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;tests for all values other than &lt;code&gt;NULL&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;

&lt;h2 id="working-with-wildcards"&gt;Working with Wildcards&lt;/h2&gt;

&lt;p&gt;SQL allows the use of wildcard characters. These are useful if you're trying to find a specific entry in a table, but aren't sure of what that entry is exactly.&lt;/p&gt;

&lt;p&gt;Asterisks (&lt;code&gt;*&lt;/code&gt;) are placeholders that represent “all,” this will query every column in a table:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;SELECT * FROM &lt;span class="highlight"&gt;table&lt;/span&gt;;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Percentage signs (&lt;code&gt;%&lt;/code&gt;) represent zero or more unknown characters.&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;SELECT * FROM &lt;span class="highlight"&gt;table&lt;/span&gt; WHERE &lt;span class="highlight"&gt;column&lt;/span&gt; LIKE &lt;span class="highlight"&gt;val%&lt;/span&gt;;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Underscores (&lt;code&gt;_&lt;/code&gt;) are used to represent a single unknown character:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;SELECT * FROM &lt;span class="highlight"&gt;table&lt;/span&gt; WHERE &lt;span class="highlight"&gt;column&lt;/span&gt; LIKE &lt;span class="highlight"&gt;v_lue&lt;/span&gt;;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="counting-entries-in-a-column"&gt;Counting Entries in a Column&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;COUNT&lt;/code&gt; function is used to find the number of entries in a given column. The following syntax will return the total number of values held in &lt;code&gt;&lt;span class="highlight"&gt;column&lt;/span&gt;&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;SELECT COUNT(&lt;span class="highlight"&gt;column&lt;/span&gt;) FROM &lt;span class="highlight"&gt;table&lt;/span&gt;;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can narrow down the results of a &lt;code&gt;COUNT&lt;/code&gt; function by appending a &lt;code&gt;WHERE&lt;/code&gt; clause, like this:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;SELECT COUNT(&lt;span class="highlight"&gt;column&lt;/span&gt;) FROM &lt;span class="highlight"&gt;table&lt;/span&gt; WHERE &lt;span class="highlight"&gt;column&lt;/span&gt;=&lt;span class="highlight"&gt;value&lt;/span&gt;;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="finding-the-average-value-in-a-column"&gt;Finding the Average Value in a Column&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;AVG&lt;/code&gt; function is used to find the average (in this case, the mean) amongst values held in a specific column. Note that the &lt;code&gt;AVG&lt;/code&gt; function will only work with columns holding numeric values; when used on a column holding string values, it may return an error or &lt;code&gt;0&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;SELECT AVG(&lt;span class="highlight"&gt;column&lt;/span&gt;) FROM &lt;span class="highlight"&gt;table&lt;/span&gt;;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="finding-the-sum-of-values-in-a-column"&gt;Finding the Sum of Values in a Column&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;SUM&lt;/code&gt; function is used to find the sum total of all the numeric values held in a column:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;SELECT SUM(&lt;span class="highlight"&gt;column&lt;/span&gt;) FROM &lt;span class="highlight"&gt;table&lt;/span&gt;;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As with the &lt;code&gt;AVG&lt;/code&gt; function, if you run the &lt;code&gt;SUM&lt;/code&gt; function on a column holding string values it may return an error or just &lt;code&gt;0&lt;/code&gt;, depending on your RDBMS. &lt;/p&gt;

&lt;h2 id="finding-the-largest-value-in-a-column"&gt;Finding the Largest Value in a Column&lt;/h2&gt;

&lt;p&gt;To find the largest numeric value in a column or the last value alphabetically, use the &lt;code&gt;MAX&lt;/code&gt; function:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;SELECT MAX(&lt;span class="highlight"&gt;column&lt;/span&gt;) FROM &lt;span class="highlight"&gt;table&lt;/span&gt;;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="finding-the-smallest-value-in-a-column"&gt;Finding the Smallest Value in a Column&lt;/h2&gt;

&lt;p&gt;To find the smallest numeric value in a column or the first value alphabetically, use the &lt;code&gt;MIN&lt;/code&gt; function:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;SELECT MIN(&lt;span class="highlight"&gt;column&lt;/span&gt;) FROM &lt;span class="highlight"&gt;table&lt;/span&gt;;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="sorting-results-with-order-by-clauses"&gt;Sorting Results with ORDER BY Clauses&lt;/h2&gt;

&lt;p&gt;An &lt;code&gt;ORDER BY&lt;/code&gt; clause is used to sort query results. The following query syntax returns the values from &lt;code&gt;&lt;span class="highlight"&gt;column_1&lt;/span&gt;&lt;/code&gt; and &lt;code&gt;&lt;span class="highlight"&gt;column_2&lt;/span&gt;&lt;/code&gt; and sorts the results by the values held in &lt;code&gt;&lt;span class="highlight"&gt;column_1&lt;/span&gt;&lt;/code&gt; in ascending order or, for string values, in alphabetical order:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;SELECT &lt;span class="highlight"&gt;column_1&lt;/span&gt;, &lt;span class="highlight"&gt;column_2&lt;/span&gt; FROM &lt;span class="highlight"&gt;table&lt;/span&gt; ORDER BY &lt;span class="highlight"&gt;column_1&lt;/span&gt;;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To perform the same action, but order the results in descending or reverse alphabetical order, append the query with &lt;code&gt;DESC&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;SELECT &lt;span class="highlight"&gt;column_1&lt;/span&gt;, &lt;span class="highlight"&gt;column_2&lt;/span&gt; FROM &lt;span class="highlight"&gt;table&lt;/span&gt; ORDER BY &lt;span class="highlight"&gt;column_1&lt;/span&gt; DESC;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="sorting-results-with-group-by-clauses"&gt;Sorting Results with GROUP BY Clauses&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;GROUP BY&lt;/code&gt; clause is similar to the &lt;code&gt;ORDER BY&lt;/code&gt; clause, but it is used to sort the results of a query that includes an aggregate function such as &lt;code&gt;COUNT&lt;/code&gt;, &lt;code&gt;MAX&lt;/code&gt;, &lt;code&gt;MIN&lt;/code&gt;, or &lt;code&gt;SUM&lt;/code&gt;. On their own, the aggregate functions described in the previous section will only return a single value. However, you can view the results of an aggregate function performed on every matching value in a column by including a &lt;code&gt;GROUP BY&lt;/code&gt; clause.&lt;/p&gt;

&lt;p&gt;The following syntax will count the number of matching values in &lt;code&gt;&lt;span class="highlight"&gt;column_2&lt;/span&gt;&lt;/code&gt; and group them in ascending or alphabetical order:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;SELECT COUNT(&lt;span class="highlight"&gt;column_1&lt;/span&gt;), &lt;span class="highlight"&gt;column_2&lt;/span&gt; FROM &lt;span class="highlight"&gt;table&lt;/span&gt; GROUP BY &lt;span class="highlight"&gt;column_2&lt;/span&gt;;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To perform the same action, but group the results in descending or reverse alphabetical order, append the query with &lt;code&gt;DESC&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;SELECT COUNT(&lt;span class="highlight"&gt;column_1&lt;/span&gt;), &lt;span class="highlight"&gt;column_2&lt;/span&gt; FROM &lt;span class="highlight"&gt;table&lt;/span&gt; GROUP BY &lt;span class="highlight"&gt;column_2&lt;/span&gt; DESC;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="querying-multiple-tables-with-join-clauses"&gt;Querying Multiple Tables with JOIN Clauses&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;JOIN&lt;/code&gt; clauses are used to create result-sets that combine rows from two or more tables. A &lt;code&gt;JOIN&lt;/code&gt; clause will only work if the two tables each have a column with an identical name and data type, as in this example: &lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;SELECT &lt;span class="highlight"&gt;table_1&lt;/span&gt;.&lt;span class="highlight"&gt;column_1&lt;/span&gt;, &lt;span class="highlight"&gt;table_2&lt;/span&gt;.&lt;span class="highlight"&gt;column_2&lt;/span&gt; FROM &lt;span class="highlight"&gt;table_1&lt;/span&gt; JOIN &lt;span class="highlight"&gt;table_2&lt;/span&gt; ON &lt;span class="highlight"&gt;table_1&lt;/span&gt;.&lt;span class="highlight"&gt;common_column&lt;/span&gt;=&lt;span class="highlight"&gt;table_2&lt;/span&gt;.&lt;span class="highlight"&gt;common_column&lt;/span&gt;;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is an example of an &lt;code&gt;INNER JOIN&lt;/code&gt; clause. An &lt;code&gt;INNER JOIN&lt;/code&gt; will return all the records that have matching values in both tables, but won't show any records that don't have matching values. &lt;/p&gt;

&lt;p&gt;It's possible to return all the records from one of two tables, including values that do not have a corresponding match in the other table, by using an &lt;em&gt;outer&lt;/em&gt; &lt;code&gt;JOIN&lt;/code&gt; clause. Outer &lt;code&gt;JOIN&lt;/code&gt; clauses are written as either &lt;code&gt;LEFT JOIN&lt;/code&gt; or &lt;code&gt;RIGHT JOIN&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;A &lt;code&gt;LEFT JOIN&lt;/code&gt; clause returns all the records from the “left” table and only the matching records from the “right” table. In the context of outer &lt;code&gt;JOIN&lt;/code&gt; clauses, the left table is the one referenced in the &lt;code&gt;FROM&lt;/code&gt; clause, and the right table is any other table referenced after the &lt;code&gt;JOIN&lt;/code&gt; statement. The following will show every record from &lt;code&gt;&lt;span class="highlight"&gt;table_1&lt;/span&gt;&lt;/code&gt; and only the matching values from &lt;code&gt;&lt;span class="highlight"&gt;table_2&lt;/span&gt;&lt;/code&gt;. Any values that do not have a match in &lt;code&gt;&lt;span class="highlight"&gt;table_2&lt;/span&gt;&lt;/code&gt; will appear as &lt;code&gt;NULL&lt;/code&gt; in the result-set:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;SELECT &lt;span class="highlight"&gt;table_1&lt;/span&gt;.&lt;span class="highlight"&gt;column_1&lt;/span&gt;, &lt;span class="highlight"&gt;table_2&lt;/span&gt;.&lt;span class="highlight"&gt;column_2&lt;/span&gt; FROM &lt;span class="highlight"&gt;table_1&lt;/span&gt; LEFT JOIN &lt;span class="highlight"&gt;table_2&lt;/span&gt; ON &lt;span class="highlight"&gt;table_1&lt;/span&gt;.&lt;span class="highlight"&gt;common_column&lt;/span&gt;=&lt;span class="highlight"&gt;table_2&lt;/span&gt;.&lt;span class="highlight"&gt;common_column&lt;/span&gt;;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A &lt;code&gt;RIGHT JOIN&lt;/code&gt; clause functions the same as a &lt;code&gt;LEFT JOIN&lt;/code&gt;, but it prints the all the results from the right table, and only the matching values from the left:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;SELECT &lt;span class="highlight"&gt;table_1&lt;/span&gt;.&lt;span class="highlight"&gt;column_1&lt;/span&gt;, &lt;span class="highlight"&gt;table_2&lt;/span&gt;.&lt;span class="highlight"&gt;column_2&lt;/span&gt; FROM &lt;span class="highlight"&gt;table_1&lt;/span&gt; RIGHT JOIN &lt;span class="highlight"&gt;table_2&lt;/span&gt; ON &lt;span class="highlight"&gt;table_1&lt;/span&gt;.&lt;span class="highlight"&gt;common_column&lt;/span&gt;=&lt;span class="highlight"&gt;table_2&lt;/span&gt;.&lt;span class="highlight"&gt;common_column&lt;/span&gt;;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="combining-multiple-select-statements-with-union-clauses"&gt;Combining Multiple SELECT Statements with UNION Clauses&lt;/h2&gt;

&lt;p&gt;A &lt;code&gt;UNION&lt;/code&gt; operator is useful for combining the results of two (or more) &lt;code&gt;SELECT&lt;/code&gt; statements into a single result-set:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;SELECT &lt;span class="highlight"&gt;column_1&lt;/span&gt; FROM &lt;span class="highlight"&gt;table&lt;/span&gt; UNION SELECT &lt;span class="highlight"&gt;column_2&lt;/span&gt; FROM &lt;span class="highlight"&gt;table&lt;/span&gt;;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Additionally, the &lt;code&gt;UNION&lt;/code&gt; clause can combine two (or more) &lt;code&gt;SELECT&lt;/code&gt; statements querying different tables into the same result-set:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;SELECT &lt;span class="highlight"&gt;column&lt;/span&gt; FROM &lt;span class="highlight"&gt;table_1&lt;/span&gt; UNION SELECT &lt;span class="highlight"&gt;column&lt;/span&gt; FROM &lt;span class="highlight"&gt;table_2&lt;/span&gt;;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;This guide covers some of the more common commands in SQL used to manage databases, users, and tables, and query the contents held in those tables. There are, however, many combinations of clauses and operators that all produce unique result-sets. If you're looking for a more comprehensive guide to working with SQL, we encourage you to check out &lt;a href="https://docs.oracle.com/cd/B19306_01/server.102/b14200/toc.htm"&gt;Oracle's Database SQL Reference&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Additionally, if there are common SQL commands you'd like to see in this guide, please ask or make suggestions in the comments below.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/webinar-series-building-blocks-for-doing-ci-cd-with-kubernetes</id>
    <published>2018-09-12T21:47:10Z</published>
    <updated>2018-09-21T17:14:41Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/webinar-series-building-blocks-for-doing-ci-cd-with-kubernetes"/>
    <title>Webinar Series: Building Blocks for Doing CI/CD with Kubernetes</title>
    <content type="html">&lt;span class='note'&gt;&lt;p&gt;&lt;/p&gt;

&lt;h2 id="webinar-series"&gt;Webinar Series&lt;/h2&gt;

&lt;p&gt;This article supplements a &lt;a href="https://go.digitalocean.com/cicd-on-k8s"&gt;webinar series on doing CI/CD with Kubernetes&lt;/a&gt;. The series discusses how to take a Cloud Native approach to building, testing, and deploying applications, covering release management, Cloud Native tools, Service Meshes, and CI/CD tools that can be used with Kubernetes. It is designed to help developers and businesses that are interested in integrating CI/CD best practices with Kubernetes into their workflows.&lt;/p&gt;

&lt;p&gt;This tutorial includes the concepts and commands from the first session of the series, Building Blocks for Doing CI/CD with Kubernetes.&lt;/p&gt;&lt;/span&gt;

&lt;p&gt;&lt;iframe width="854" height="480" src="//www.youtube.com/embed/XxxlmkKdw6M?rel=0" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;/p&gt;

&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;If you are getting started with &lt;a href="https://www.digitalocean.com/community/tutorials/webinar-series-getting-started-with-containers"&gt;containers&lt;/a&gt;, you will likely want to know how to automate building, testing, and deployment. By taking a &lt;a href="https://github.com/cncf/toc/blob/master/DEFINITION.md"&gt;Cloud Native&lt;/a&gt; approach to these processes, you can leverage the right infrastructure APIs to package and deploy applications in an automated way. &lt;/p&gt;

&lt;p&gt;Two building blocks for doing automation include &lt;em&gt;container images&lt;/em&gt; and &lt;em&gt;container orchestrators&lt;/em&gt;.  Over the last year or so, &lt;a href="https://kubernetes.io/"&gt;Kubernetes&lt;/a&gt; has become the default choice for container orchestration.  In this first article of the &lt;strong&gt;CI/CD with Kubernetes&lt;/strong&gt; series, you will: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Build container images with &lt;a href="https://www.docker.com/"&gt;Docker&lt;/a&gt;, &lt;a href="https://github.com/projectatomic/buildah"&gt;Buildah&lt;/a&gt;, and &lt;a href="https://github.com/GoogleContainerTools/kaniko"&gt;Kaniko&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Set up a Kubernetes cluster with &lt;a href="https://www.terraform.io/"&gt;Terraform&lt;/a&gt;, and create &lt;em&gt;Deployments&lt;/em&gt; and &lt;em&gt;Services&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Extend the functionality of a Kubernetes cluster with &lt;em&gt;Custom Resources&lt;/em&gt;. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By the end of this tutorial, you will have container images built with Docker, Buildah, and Kaniko, and a Kubernetes cluster with Deployments, Services, and Custom Resources. &lt;/p&gt;

&lt;p&gt;Future articles in the series will cover related topics: package management for Kubernetes, CI/CD tools like &lt;a href="https://jenkins-x.io/"&gt;Jenkins X&lt;/a&gt; and &lt;a href="https://www.spinnaker.io/"&gt;Spinnaker&lt;/a&gt;, Services Meshes, and GitOps. &lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;An Ubuntu 16.04 server with a non-root user account. Follow our &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-16-04"&gt;Initial Server Setup with Ubuntu 16.04&lt;/a&gt; tutorial for guidance.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.docker.com/"&gt;Docker&lt;/a&gt; installed on your server. Please follow Steps 1 and 2 of &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-and-use-docker-on-ubuntu-16-04"&gt;How To Install and Use Docker on Ubuntu 16.04&lt;/a&gt; for installation instructions.&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://hub.docker.com"&gt;Docker Hub account&lt;/a&gt;. For an overview on getting started with Docker Hub, please see &lt;a href="https://docs.docker.com/docker-hub/"&gt;these instructions&lt;/a&gt;.&lt;br&gt;&lt;/li&gt;
&lt;li&gt;A DigitalOcean account and personal access token. Please refer to &lt;a href="https://www.digitalocean.com/docs/api/create-personal-access-token/"&gt;these instructions&lt;/a&gt; to get your access token. &lt;/li&gt;
&lt;li&gt;Familiarity with containers and Docker. Please refer to the &lt;a href="https://www.digitalocean.com/community/tutorials/webinar-series-getting-started-with-containers"&gt;Webinar Series: Getting Started with Containers&lt;/a&gt; for more details. &lt;/li&gt;
&lt;li&gt;Familiarity with Kubernetes concepts. Please refer to &lt;a href="https://www.digitalocean.com/community/tutorials/an-introduction-to-kubernetes"&gt;An Introduction to Kubernetes&lt;/a&gt; for more details. &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="step-1-—-building-container-images-with-docker-and-buildah"&gt;Step 1 — Building Container Images with Docker and Buildah&lt;/h2&gt;

&lt;p&gt;A container image is a self-contained entity with its own application code, runtime, and dependencies that you can use to create and run containers. You can use different tools to create container images, and in this step you will build containers with two of them: Docker and Buildah. &lt;/p&gt;

&lt;h3 id="building-container-images-with-dockerfiles"&gt;Building Container Images with Dockerfiles&lt;/h3&gt;

&lt;p&gt;Docker builds your container images automatically by reading instructions from a Dockerfile, a text file that includes the commands required to assemble a container image. Using the &lt;code&gt;docker image build&lt;/code&gt; command, you can create an automated build that will execute the command-line instructions provided in the Dockerfile.  When building the image, you will also pass the &lt;em&gt;build context&lt;/em&gt; with the Dockerfile, which contains the set of files required to create an environment and run an application in the container image.&lt;/p&gt;

&lt;p&gt;Typically, you will create a project folder for your Dockerfile and build context. Create a folder called &lt;code&gt;&lt;span class="highlight"&gt;demo&lt;/span&gt;&lt;/code&gt; to begin:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;mkdir &lt;span class="highlight"&gt;demo&lt;/span&gt;
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;cd &lt;span class="highlight"&gt;demo&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, create a Dockerfile inside the &lt;code&gt;demo&lt;/code&gt; folder: &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nano Dockerfile
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add the following content to the file:&lt;/p&gt;
&lt;div class="code-label " title="~/demo/Dockerfile"&gt;~/demo/Dockerfile&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;FROM ubuntu:16.04

LABEL MAINTAINER neependra@cloudyuga.guru

RUN apt-get update \
    &amp;amp;&amp;amp; apt-get install -y nginx \
    &amp;amp;&amp;amp; apt-get clean \
    &amp;amp;&amp;amp; rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* \
    &amp;amp;&amp;amp; echo "daemon off;" &amp;gt;&amp;gt; /etc/nginx/nginx.conf

EXPOSE 80
CMD ["nginx"]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This Dockerfile consists of a set of instructions that will build an image to run Nginx.  During the build process &lt;code&gt;ubuntu:16.04&lt;/code&gt; will function as the base image, and the &lt;code&gt;nginx&lt;/code&gt; package will be installed. Using the &lt;code&gt;CMD&lt;/code&gt; instruction, you've also configured &lt;code&gt;nginx&lt;/code&gt; to be the default command when the container starts. &lt;/p&gt;

&lt;p&gt;Next, you'll build the container image with the &lt;code&gt;docker image build&lt;/code&gt; command, using the current directory (.) as the build context. Passing the &lt;code&gt;-t&lt;/code&gt; option to this command names the image &lt;code&gt;&lt;span class="highlight"&gt;nkhare/nginx:latest&lt;/span&gt;&lt;/code&gt;: &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo docker image build -t &lt;span class="highlight"&gt;nkhare/nginx:latest&lt;/span&gt; .
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will see the following output:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Sending build context to Docker daemon  49.25MB
Step 1/5 : FROM ubuntu:16.04
 ---&amp;gt; 7aa3602ab41e
Step 2/5 : MAINTAINER neependra@cloudyuga.guru
 ---&amp;gt; Using cache
 ---&amp;gt; 552b90c2ff8d
Step 3/5 : RUN apt-get update     &amp;amp;&amp;amp; apt-get install -y nginx     &amp;amp;&amp;amp; apt-get clean     &amp;amp;&amp;amp; rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*     &amp;amp;&amp;amp; echo "daemon off;" &amp;gt;&amp;gt; /etc/nginx/nginx.conf
 ---&amp;gt; Using cache
 ---&amp;gt; 6bea966278d8
Step 4/5 : EXPOSE 80
 ---&amp;gt; Using cache
 ---&amp;gt; 8f1c4281309e
Step 5/5 : CMD ["nginx"]
 ---&amp;gt; Using cache
 ---&amp;gt; f545da818f47
Successfully built f545da818f47
Successfully tagged nginx:latest
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Your image is now built. You can list your Docker images using the following command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;docker image ls
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
nkhare/nginx        latest              4073540cbcec        3 seconds ago       171MB
ubuntu              16.04               7aa3602ab41e        11 days ago     
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can now use the &lt;code&gt;nkhare/nginx:latest&lt;/code&gt; image to create containers. &lt;/p&gt;

&lt;h3 id="building-container-images-with-project-atomic-buildah"&gt;Building Container Images with Project Atomic-Buildah&lt;/h3&gt;

&lt;p&gt;Buildah is a CLI tool, developed by &lt;a href="https://github.com/projectatomic/"&gt;Project Atomic&lt;/a&gt;, for quickly building &lt;em&gt;Open Container Initiative (&lt;a href="https://www.opencontainers.org"&gt;OCI&lt;/a&gt;)&lt;/em&gt;-compliant images. OCI provides specifications for container runtimes and images in an effort to standardize industry best practices. &lt;/p&gt;

&lt;p&gt;Buildah can create an image either from a working container or from a Dockerfile. It can build images completely in user space without the Docker daemon, and can perform image operations like &lt;code&gt;build&lt;/code&gt;, &lt;code&gt;list&lt;/code&gt;, &lt;code&gt;push&lt;/code&gt;, and &lt;code&gt;tag&lt;/code&gt;. In this step, you'll compile Buildah from source and then use it to create a container image. &lt;/p&gt;

&lt;p&gt;To install Buildah you will need the required dependencies, including tools that will enable you to manage packages and package security, among other things. Run the following commands to install these packages:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt; cd
&lt;/li&gt;&lt;li class="line" prefix="$"&gt; sudo apt-get install software-properties-common
&lt;/li&gt;&lt;li class="line" prefix="$"&gt; sudo add-apt-repository ppa:alexlarsson/flatpak
&lt;/li&gt;&lt;li class="line" prefix="$"&gt; sudo add-apt-repository ppa:gophers/archive
&lt;/li&gt;&lt;li class="line" prefix="$"&gt; sudo apt-add-repository ppa:projectatomic/ppa
&lt;/li&gt;&lt;li class="line" prefix="$"&gt; sudo apt-get update
&lt;/li&gt;&lt;li class="line" prefix="$"&gt; sudo apt-get install bats btrfs-tools git libapparmor-dev libdevmapper-dev libglib2.0-dev libgpgme11-dev libostree-dev libseccomp-dev libselinux1-dev skopeo-containers go-md2man
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Because you will compile the &lt;code&gt;buildah&lt;/code&gt; source code to create its package, you'll also need to install &lt;a href="https://golang.org/"&gt;Go&lt;/a&gt;:  &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt-get update
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo curl -O https://storage.googleapis.com/golang/go1.8.linux-amd64.tar.gz
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo tar -xvf go1.8.linux-amd64.tar.gz
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo mv go /usr/local
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo echo 'export PATH=$PATH:/usr/local/go/bin' &amp;gt;&amp;gt; ~/.profile
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;source ~/.profile
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;go version 
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will see the following output, indicating a successful installation:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;go version go&lt;span class="highlight"&gt;1.8&lt;/span&gt; linux/amd64
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can now get the &lt;code&gt;buildah&lt;/code&gt; source code to create its package, along with the &lt;code&gt;runc&lt;/code&gt; binary. &lt;code&gt;runc&lt;/code&gt; is the implementation of the &lt;code&gt;OCI&lt;/code&gt; container runtime, which you will use to run your Buildah containers.  &lt;/p&gt;

&lt;p&gt;Run the following commands to install &lt;code&gt;runc&lt;/code&gt; and &lt;code&gt;buildah&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;mkdir ~/buildah
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;cd ~/buildah
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;export GOPATH=`pwd`
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;git clone https://github.com/projectatomic/buildah ./src/github.com/projectatomic/buildah
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;cd ./src/github.com/projectatomic/buildah
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;make runc all TAGS="apparmor seccomp"
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo cp ~/buildah/src/github.com/opencontainers/runc/runc /usr/bin/.
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo apt install buildah 
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, create the &lt;code&gt;/etc/containers/registries.conf&lt;/code&gt; file to configure your container registries:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/containers/registries.conf
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add the following content to the file to specify your registries:&lt;/p&gt;
&lt;div class="code-label " title="/etc/containers/registries.conf"&gt;/etc/containers/registries.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;
# This is a system-wide configuration file used to
# keep track of registries for various container backends.
# It adheres to TOML format and does not support recursive
# lists of registries.

# The default location for this configuration file is /etc/containers/registries.conf.

# The only valid categories are: 'registries.search', 'registries.insecure',
# and 'registries.block'.

[registries.search]
registries = ['docker.io', 'registry.fedoraproject.org', 'quay.io', 'registry.access.redhat.com', 'registry.centos.org']

# If you need to access insecure registries, add the registry's fully-qualified name.
# An insecure registry is one that does not have a valid SSL certificate or only does HTTP.
[registries.insecure]
registries = []

# If you need to block pull access from a registry, uncomment the section below
# and add the registries fully-qualified name.
#
# Docker only
[registries.block]
registries = []
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;registries.conf&lt;/code&gt; configuration file specifies which registries should be consulted when completing image names that do not include a registry or domain portion.&lt;/p&gt;

&lt;p&gt;Now run the following command to build an image, using the &lt;code&gt;https://github.com/do-community/rsvpapp&lt;/code&gt; repository as the build context. This repository also contains the relevant Dockerfile:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo buildah build-using-dockerfile -t rsvpapp:buildah github.com/do-community/rsvpapp 
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This command creates an image named &lt;code&gt;rsvpapp:buildah&lt;/code&gt; from the Dockerfille available in the &lt;code&gt;https://github.com/do-community/rsvpapp&lt;/code&gt; repository. &lt;/p&gt;

&lt;p&gt;To list the images, use the following command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo buildah images
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will see the following output:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;IMAGE ID             IMAGE NAME                                               CREATED AT             SIZE
b0c552b8cf64         docker.io/teamcloudyuga/python:alpine                    Sep 30, 2016 04:39     95.3 MB
&lt;span class="highlight"&gt;22121fd251df         localhost/rsvpapp:buildah                                Sep 11, 2018 14:34     114 MB&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;One of these images is &lt;code&gt;localhost/rsvpapp:buildah&lt;/code&gt;, which you just created. The other, &lt;code&gt;docker.io/teamcloudyuga/python:alpine&lt;/code&gt;, is the base image from the Dockerfile. &lt;/p&gt;

&lt;p&gt;Once you have built the image, you can push it to Docker Hub. This will allow you to store it for future use. You will first need to login to your Docker Hub account from the command line:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;docker login -u &lt;span class="highlight"&gt;your-dockerhub-username&lt;/span&gt; -p &lt;span class="highlight"&gt;your-dockerhub-password&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once the login is successful, you will get a file, &lt;code&gt;~/.docker/config.json&lt;/code&gt;, that will contain your Docker Hub credentials. You can then use that file with &lt;code&gt;buildah&lt;/code&gt; to push images to Docker Hub.&lt;/p&gt;

&lt;p&gt;For example, if you wanted to push the image you just created, you could run the following command, citing the &lt;code&gt;authfile&lt;/code&gt; and the image to push: &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo buildah push --authfile ~/.docker/config.json rsvpapp:buildah docker://&lt;span class="highlight"&gt;your-dockerhub-username&lt;/span&gt;/rsvpapp:buildah
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can also push the resulting image to the local Docker daemon using the following command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo buildah push rsvpapp:buildah docker-daemon:rsvpapp:buildah
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, take a look at the Docker images you have created: &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo docker image ls
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
&lt;span class="highlight"&gt;rsvpapp             buildah             22121fd251df        4 minutes ago       108MB&lt;/span&gt;
nkhare/nginx        latest              01f0982d91b8        17 minutes ago      172MB
ubuntu              16.04               b9e15a5d1e1a        5 days ago          115MB
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As expected, you should now see a new image, &lt;code&gt;rsvpapp:buildah&lt;/code&gt;, that has been exported using &lt;code&gt;buildah&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;You now have experience building container images with two different tools, Docker and Buildah. Let's move on to discussing how to set up a cluster of containers with Kubernetes.&lt;/p&gt;

&lt;h2 id="step-2-—-setting-up-a-kubernetes-cluster-on-digitalocean-using-kubeadm-and-terraform"&gt;Step 2 — Setting Up a Kubernetes Cluster on DigitalOcean using kubeadm and Terraform&lt;/h2&gt;

&lt;p&gt;There are different ways to set up Kubernetes on DigitalOcean. To learn more about how to set up Kubernetes with &lt;a href="https://kubernetes.io/docs/reference/setup-tools/kubeadm/kubeadm/"&gt;kubeadm&lt;/a&gt;, for example, you can look at &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-create-a-kubernetes-1-11-cluster-using-kubeadm-on-ubuntu-18-04"&gt;How To Create a Kubernetes Cluster Using Kubeadm on Ubuntu 18.04&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Since this tutorial series discusses taking a Cloud Native approach to application development, we'll apply this methodology when setting up our cluster. Specifically, we will automate our cluster creation using kubeadm and &lt;a href="https://www.terraform.io/"&gt;Terraform&lt;/a&gt;, a tool that simplifies creating and changing infrastructure. &lt;/p&gt;

&lt;p&gt;Using your personal access token, you will connect to DigitalOcean with Terraform to provision 3 servers. You will run the &lt;code&gt;kubeadm&lt;/code&gt; commands inside of these VMs to create a 3-node Kubernetes cluster containing one master node and two workers. &lt;/p&gt;

&lt;p&gt;On your Ubuntu server, create a pair of &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-configure-ssh-key-based-authentication-on-a-linux-server"&gt;SSH keys&lt;/a&gt;, which will allow password-less logins to your VMs: &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;ssh-keygen -t rsa
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will see the following output:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Generating public/private rsa key pair.
Enter file in which to save the key (~/.ssh/id_rsa): 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Press &lt;code&gt;ENTER&lt;/code&gt; to save the key pair in the &lt;code&gt;~/.ssh&lt;/code&gt; directory in your home directory, or enter another destination. &lt;/p&gt;

&lt;p&gt;Next, you will see the following prompt: &lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Enter passphrase (empty for no passphrase): 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this case, press &lt;code&gt;ENTER&lt;/code&gt; without a password to enable password-less logins to your nodes.&lt;/p&gt;

&lt;p&gt;You will see a confirmation that your key pair has been created:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Your identification has been saved in ~/.ssh/id_rsa.
Your public key has been saved in ~/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:lCVaexVBIwHo++NlIxccMW5b6QAJa+ZEr9ogAElUFyY root@3b9a273f18b5
The key's randomart image is:
+---[RSA 2048]----+
|++.E ++o=o*o*o   |
|o   +..=.B = o   |
|.    .* = * o    |
| .   =.o + *     |
|  . . o.S + .    |
|   . +.    .     |
|    . ... =      |
|        o= .     |
|       ...       |
+----[SHA256]-----+
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Get your public key by running the following command, which will display it in your terminal:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cat ~/.ssh/id_rsa.pub
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add this key to your DigitalOcean account by following &lt;a href="https://www.digitalocean.com/docs/droplets/how-to/add-ssh-keys/to-account/"&gt;these directions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Next, install Terraform:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt-get update
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo apt-get install unzip
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;wget https://releases.hashicorp.com/terraform/0.11.7/terraform_0.11.7_linux_amd64.zip
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;unzip terraform_0.11.7_linux_amd64.zip
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo mv terraform /usr/bin/.
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;terraform version
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will see output confirming your Terraform installation:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Terraform v&lt;span class="highlight"&gt;0.11.7&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, run the following commands to install &lt;code&gt;kubectl&lt;/code&gt;, a CLI tool that will communicate with your Kubernetes cluster, and to create a &lt;code&gt;~/.kube&lt;/code&gt; directory in your user's home directory:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt-get install apt-transport-https
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo touch /etc/apt/sources.list.d/kubernetes.list 
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;echo "deb http://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee -a /etc/apt/sources.list.d/kubernetes.list
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo apt-get update
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo apt-get install kubectl
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;mkdir -p ~/.kube
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Creating the &lt;code&gt;~/.kube&lt;/code&gt; directory will enable you to copy the configuration file to this location. You’ll do that once you run the Kubernetes setup script later in this section. By default, the &lt;code&gt;kubectl&lt;/code&gt; CLI looks for the configuration file in the &lt;code&gt;~/.kube&lt;/code&gt; directory to access the cluster. &lt;/p&gt;

&lt;p&gt;Next, clone the sample project repository for this tutorial, which contains the Terraform scripts for setting up the infrastructure:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;git clone https://github.com/do-community/k8s-cicd-webinars.git
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Go to the Terrafrom script directory:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cd k8s-cicd-webinars/webinar1/2-kubernetes/1-Terraform/
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Get a fingerprint of your SSH public key:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;ssh-keygen -E md5 -lf ~/.ssh/id_rsa.pub | awk '{print $2}'
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will see output like the following, with the highlighted portion representing your key:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;MD5:&lt;span class="highlight"&gt;dd:d1:b7:0f:6d:30:c0:be:ed:ae:c7:b9:b8:4a:df:5e&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Keep in mind that your key will differ from what's shown here.&lt;/p&gt;

&lt;p&gt;Save the fingerprint to an environmental variable so Terraform can use it:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;export FINGERPRINT=&lt;span class="highlight"&gt;dd:d1:b7:0f:6d:30:c0:be:ed:ae:c7:b9:b8:4a:df:5e&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, export your DO personal access token:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;export TOKEN=&lt;span class="highlight"&gt;your-do-access-token&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now take a look at the &lt;code&gt;~/k8s-cicd-webinars/webinar1/2-kubernetes/1-Terraform/&lt;/code&gt; project directory: &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;ls
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;cluster.tf  destroy.sh  files outputs.tf  provider.tf  script.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This folder contains the necessary scripts and configuration files for deploying your Kubernetes cluster with Terraform. &lt;/p&gt;

&lt;p&gt;Execute the &lt;code&gt;script.sh&lt;/code&gt; script to trigger the Kubernetes cluster setup:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;./script.sh
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When the script execution is complete, &lt;code&gt;kubectl&lt;/code&gt; will be configured to use the Kubernetes cluster you've created.  &lt;/p&gt;

&lt;p&gt;List the cluster nodes using &lt;code&gt;kubectl get nodes&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl get nodes
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;NAME                STATUS    ROLES     AGE       VERSION
k8s-master-node     Ready     master    2m        v1.10.0
k8s-worker-node-1   Ready     &amp;lt;none&amp;gt;    1m        v1.10.0
k8s-worker-node-2   Ready     &amp;lt;none&amp;gt;    57s       v1.10.0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You now have one master and two worker nodes in the &lt;code&gt;Ready&lt;/code&gt; state.  &lt;/p&gt;

&lt;p&gt;With a Kubernetes cluster set up, you can now explore another option for building container images: &lt;a href="https://github.com/GoogleContainerTools/kaniko"&gt;Kaniko from Google&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id="step-3-—-building-container-images-with-kaniko"&gt;Step 3 — Building Container Images with Kaniko&lt;/h2&gt;

&lt;p&gt;Earlier in this tutorial, you built container images with Dockerfiles and Buildah. But what if you could build container images directly on Kubernetes? There are ways to run the &lt;code&gt;docker image build&lt;/code&gt; command inside of Kubernetes, but this isn't native Kubernetes tooling. You would have to depend on the Docker daemon to build images, and it would need to run on one of the &lt;a href="https://kubernetes.io/docs/concepts/workloads/pods/pod/"&gt;&lt;em&gt;Pods&lt;/em&gt;&lt;/a&gt; in the cluster. &lt;/p&gt;

&lt;p&gt;A tool called Kaniko allows you to build container images with a Dockerfile on an existing Kubernetes cluster. In this step, you will build a container image with a Dockerfile using Kaniko. You will then push this image to Docker Hub. &lt;/p&gt;

&lt;p&gt;In order to push your image to Docker Hub, you will need to pass your Docker Hub credentials to Kaniko. In the previous step, you logged into Docker Hub and created a &lt;code&gt;~/.docker/config.json&lt;/code&gt; file with your login credentials. Let's use this configuration file to create a Kubernetes &lt;em&gt;ConfigMap&lt;/em&gt; object to store the credentials inside the Kubernetes cluster. The ConfigMap object is used to store configuration parameters, decoupling them from your application. &lt;/p&gt;

&lt;p&gt;To create a ConfigMap called &lt;code&gt;docker-config&lt;/code&gt; using the &lt;code&gt;~/.docker/config.json&lt;/code&gt; file, run the following command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo kubectl create configmap docker-config --from-file=$HOME/.docker/config.json
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, you can create a Pod definition file called &lt;code&gt;pod-kaniko.yml&lt;/code&gt; in the &lt;code&gt;~/k8s-cicd-webinars/webinar1/2-kubernetes/1-Terraform/&lt;/code&gt;  directory (though it can go anywhere). &lt;/p&gt;

&lt;p&gt;First, make sure that you are in the &lt;code&gt;~/k8s-cicd-webinars/webinar1/2-kubernetes/1-Terraform/&lt;/code&gt; directory:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cd ~/k8s-cicd-webinars/webinar1/2-kubernetes/1-Terraform/
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Create the &lt;code&gt;pod-kaniko.yml&lt;/code&gt; file:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nano pod-kaniko.yml
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add the following content to the file to specify what will happen when you deploy your Pod. Be sure to replace &lt;code&gt;&lt;span class="highlight"&gt;your-dockerhub-username&lt;/span&gt;&lt;/code&gt; in the Pod's &lt;code&gt;args&lt;/code&gt; field with your own Docker Hub username:&lt;/p&gt;
&lt;div class="code-label " title="~/k8s-cicd-webinars/webinar1/2-kubernetes/1-Terraform/pod-kaniko.yaml"&gt;~/k8s-cicd-webinars/webinar1/2-kubernetes/1-Terraform/pod-kaniko.yaml&lt;/div&gt;&lt;pre class="code-pre yaml"&gt;&lt;code langs=""&gt;apiVersion: v1
kind: Pod
metadata:
  name: kaniko
spec:
  containers:
  - name: kaniko
    image: gcr.io/kaniko-project/executor:latest
    args: ["--dockerfile=./Dockerfile",
            "--context=/tmp/rsvpapp/",
            "--destination=docker.io/&lt;span class="highlight"&gt;your-dockerhub-username&lt;/span&gt;/rsvpapp:kaniko",
            "--force" ]
    volumeMounts:
      - name: docker-config
        mountPath: /root/.docker/
      - name: demo
        mountPath: /tmp/rsvpapp
  restartPolicy: Never
  initContainers:
    - image: python
      name: demo
      command: ["/bin/sh"]
      args: ["-c", "git clone https://github.com/do-community/rsvpapp.git /tmp/rsvpapp"] 
      volumeMounts:
      - name: demo
        mountPath: /tmp/rsvpapp
  restartPolicy: Never
  volumes:
    - name: docker-config
      configMap:
        name: docker-config
    - name: demo
      emptyDir: {}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This configuration file describes what will happen when your Pod is deployed. First, the &lt;a href="https://kubernetes.io/docs/concepts/workloads/pods/init-containers/"&gt;Init container&lt;/a&gt; will clone the Git repository with the Dockerfile, &lt;code&gt;https://github.com/do-community/rsvpapp.git&lt;/code&gt;, into a shared volume called &lt;code&gt;demo&lt;/code&gt;. Init containers run before application containers and can be used to run utilties or other tasks that are not desirable to run from your application containers. Your application container, &lt;code&gt;kaniko&lt;/code&gt;, will then build the image using the Dockerfile and push the resulting image to Docker Hub, using the credentials you passed to the ConfigMap volume &lt;code&gt;docker-config&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;To deploy the &lt;code&gt;kaniko&lt;/code&gt; pod, run the following command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl apply -f pod-kaniko.yml 
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will see the following confirmation:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;pod/kaniko created
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Get the list of pods:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl get pods
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will see the following list:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;NAME      READY     STATUS     RESTARTS   AGE
kaniko    0/1       Init:0/1   0          47s
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Wait a few seconds, and then run &lt;code&gt;kubectl get pods&lt;/code&gt; again for a status update: &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl get pods
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will see the following:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;NAME      READY     STATUS    RESTARTS   AGE
kaniko    1/1       Running   0          1m
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, run &lt;code&gt;kubectl get pods&lt;/code&gt; once more for a final status update:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl get pods
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;NAME      READY     STATUS      RESTARTS   AGE
kaniko    0/1       Completed   0          2m
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This sequence of output tells you that the Init container ran, cloning the GitHub repository inside of the &lt;code&gt;demo&lt;/code&gt; volume. After that, the Kaniko build process ran and eventually finished.&lt;/p&gt;

&lt;p&gt;Check the logs of the pod:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl logs kaniko
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will see the following output:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;time="2018-08-02T05:01:24Z" level=info msg="appending to multi args docker.io/&lt;span class="highlight"&gt;your-dockerhub-username&lt;/span&gt;/rsvpapp:kaniko"
time="2018-08-02T05:01:24Z" level=info msg="Downloading base image nkhare/python:alpine"
.
.
.
ime="2018-08-02T05:01:46Z" level=info msg="Taking snapshot of full filesystem..."
time="2018-08-02T05:01:48Z" level=info msg="cmd: CMD"
time="2018-08-02T05:01:48Z" level=info msg="Replacing CMD in config with [/bin/sh -c python rsvp.py]"
time="2018-08-02T05:01:48Z" level=info msg="Taking snapshot of full filesystem..."
time="2018-08-02T05:01:49Z" level=info msg="No files were changed, appending empty layer to config."
2018/08/02 05:01:51 mounted blob: sha256:bc4d09b6c77b25d6d3891095ef3b0f87fbe90621bff2a333f9b7f242299e0cfd
2018/08/02 05:01:51 mounted blob: sha256:809f49334738c14d17682456fd3629207124c4fad3c28f04618cc154d22e845b
2018/08/02 05:01:51 mounted blob: sha256:c0cb142e43453ebb1f82b905aa472e6e66017efd43872135bc5372e4fac04031
2018/08/02 05:01:51 mounted blob: sha256:606abda6711f8f4b91bbb139f8f0da67866c33378a6dcac958b2ddc54f0befd2
2018/08/02 05:01:52 pushed blob sha256:16d1686835faa5f81d67c0e87eb76eab316e1e9cd85167b292b9fa9434ad56bf
2018/08/02 05:01:53 pushed blob sha256:358d117a9400cee075514a286575d7d6ed86d118621e8b446cbb39cc5a07303b
2018/08/02 05:01:55 pushed blob sha256:5d171e492a9b691a49820bebfc25b29e53f5972ff7f14637975de9b385145e04
2018/08/02 05:01:56 index.docker.io/&lt;span class="highlight"&gt;your-dockerhub-username&lt;/span&gt;/rsvpapp:kaniko: digest: sha256:831b214cdb7f8231e55afbba40914402b6c915ef4a0a2b6cbfe9efb223522988 size: 1243
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;From the logs, you can see that the &lt;code&gt;kaniko&lt;/code&gt; container built the image from the Dockerfile and pushed it to your Docker Hub account. &lt;/p&gt;

&lt;p&gt;You can now pull the Docker image. Be sure again to replace &lt;code&gt;&lt;span class="highlight"&gt;your-dockerhub-username&lt;/span&gt;&lt;/code&gt; with your Docker Hub username:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;docker pull &lt;span class="highlight"&gt;your-dockerhub-username&lt;/span&gt;/rsvpapp:kaniko
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will see a confirmation of the pull:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;kaniko: Pulling from &lt;span class="highlight"&gt;your-dockerhub-username&lt;/span&gt;/rsvpapp
c0cb142e4345: Pull complete 
bc4d09b6c77b: Pull complete 
606abda6711f: Pull complete 
809f49334738: Pull complete 
358d117a9400: Pull complete 
5d171e492a9b: Pull complete 
Digest: sha256:831b214cdb7f8231e55afbba40914402b6c915ef4a0a2b6cbfe9efb223522988
Status: Downloaded newer image for &lt;span class="highlight"&gt;your-dockerhub-username&lt;/span&gt;/rsvpapp:kaniko
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You have now successfully built a Kubernetes cluster and created new images from within the cluster. Let's move on to discussing &lt;em&gt;Deployments&lt;/em&gt; and &lt;em&gt;Services&lt;/em&gt;. &lt;/p&gt;

&lt;h2 id="step-4-—-create-kubernetes-deployments-and-services"&gt;Step 4 — Create Kubernetes Deployments and Services&lt;/h2&gt;

&lt;p&gt;Kubernetes &lt;em&gt;Deployments&lt;/em&gt; allow you to run your applications. Deployments specify the desired state for your Pods, ensuring consistency across your rollouts. In this step, you will create an &lt;a href="https://www.nginx.com/"&gt;Nginx&lt;/a&gt; deployment file called &lt;code&gt;deployment.yml&lt;/code&gt; in the &lt;code&gt;~/k8s-cicd-webinars/webinar1/2-kubernetes/1-Terraform/&lt;/code&gt; directory to create an Nginx Deployment.&lt;/p&gt;

&lt;p&gt;First, open the file:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nano deployment.yml
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add the following configuration to the file to define your Nginx Deployment: &lt;/p&gt;
&lt;div class="code-label " title="~/k8s-cicd-webinars/webinar1/2-kubernetes/1-Terraform/deployment.yml"&gt;~/k8s-cicd-webinars/webinar1/2-kubernetes/1-Terraform/deployment.yml&lt;/div&gt;&lt;pre class="code-pre yml"&gt;&lt;code langs=""&gt;apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9
        ports:
        - containerPort: 80

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This file defines a Deployment named &lt;code&gt;nginx-deployment&lt;/code&gt; that creates three pods, each running an &lt;code&gt;nginx&lt;/code&gt; container on port &lt;code&gt;80&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To deploy the Deployment, run the following command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl apply -f deployment.yml
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will see a confirmation that the Deployment was created:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;deployment.apps/nginx-deployment created
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;List your Deployments:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl get deployments
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;NAME               DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   3         3         3            3           29s
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can see that the &lt;code&gt;nginx-deployment&lt;/code&gt; Deployment has been created and the desired and current count of the Pods are same: &lt;code&gt;3&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;To list the Pods that the Deployment created, run the following command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl get pods
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;NAME                                READY     STATUS      RESTARTS   AGE
kaniko                              0/1       Completed   0          9m
nginx-deployment-75675f5897-nhwsp   1/1       Running     0          1m
nginx-deployment-75675f5897-pxpl9   1/1       Running     0          1m
nginx-deployment-75675f5897-xvf4f   1/1       Running     0          1m
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can see from this output that the desired number of Pods are running.&lt;/p&gt;

&lt;p&gt;To expose an application deployment internally and externally, you will need to create a Kubernetes object called a &lt;em&gt;Service&lt;/em&gt;. Each Service specifies a &lt;em&gt;ServiceType&lt;/em&gt;, which defines how the service is exposed. In this example, we will use a &lt;em&gt;NodePort&lt;/em&gt; ServiceType, which exposes the Service on a static port on each node. &lt;/p&gt;

&lt;p&gt;To do this, create a file, &lt;code&gt;service.yml&lt;/code&gt;, in the &lt;code&gt;~/k8s-cicd-webinars/webinar1/2-kubernetes/1-Terrafrom/&lt;/code&gt; directory:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nano service.yml
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add the following content to define your Service:&lt;/p&gt;
&lt;div class="code-label " title="~/k8s-cicd-webinars/webinar1/2-kubernetes/1-Terrafrom/service.yml"&gt;~/k8s-cicd-webinars/webinar1/2-kubernetes/1-Terrafrom/service.yml&lt;/div&gt;&lt;pre class="code-pre yml"&gt;&lt;code langs=""&gt;kind: Service
apiVersion: v1
metadata:
  name: nginx-service
spec:
  selector:
    app: nginx
  type: NodePort
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80
    nodePort: 30111
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These settings define the Service, &lt;code&gt;nginx-service&lt;/code&gt;, and specify that it will target port &lt;code&gt;80&lt;/code&gt; on your Pod. &lt;code&gt;nodePort&lt;/code&gt; defines the port where the application will accept external traffic. &lt;/p&gt;

&lt;p&gt;To deploy the Service run the following command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl apply -f service.yml
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will see a confirmation:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;service/nginx-service created
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;List the Services:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl get service
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will see the following list: &lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;NAME            TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
kubernetes      ClusterIP   10.96.0.1       &amp;lt;none&amp;gt;        443/TCP        5h
nginx-service   NodePort    10.100.98.213   &amp;lt;none&amp;gt;        80:&lt;span class="highlight"&gt;30111&lt;/span&gt;/TCP   7s
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Your Service, &lt;code&gt;nginx-service&lt;/code&gt;, is exposed on port &lt;code&gt;30111&lt;/code&gt; and you can now access it on any of the node’s public IPs. For example, navigating to &lt;code&gt;http://&lt;span class="highlight"&gt;node_1_ip&lt;/span&gt;:30111&lt;/code&gt; or &lt;code&gt;http://&lt;span class="highlight"&gt;node_2_ip&lt;/span&gt;:30111&lt;/code&gt; should take you to Nginx's standard welcome page.&lt;/p&gt;

&lt;p&gt;Once you have tested the Deployment, you can clean up both the Deployment and Service:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl delete deployment nginx-deployment
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;kubectl delete service nginx-service
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These commands will delete the Deployment and Service you have created. &lt;/p&gt;

&lt;p&gt;Now that you have worked with Deployments and Services, let's move on to creating Custom Resources.&lt;/p&gt;

&lt;h2 id="step-5-—-creating-custom-resources-in-kubernetes"&gt;Step 5 — Creating Custom Resources in Kubernetes&lt;/h2&gt;

&lt;p&gt;Kubernetes offers limited but production-ready functionalities and features. It is possible to extend Kubernetes' offerings, however, using its &lt;a href="https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/"&gt;Custom Resources&lt;/a&gt; feature. In Kubernetes, a &lt;em&gt;resource&lt;/em&gt; is an endpoint in the Kubernetes API that stores a collection of API &lt;em&gt;objects&lt;/em&gt;. A Pod resource contains a collection of Pod objects, for instance. With Custom Resources, you can add custom offerings for networking, storage, and more. These additions can be created or removed at any point. &lt;/p&gt;

&lt;p&gt;In addition to creating custom objects, you can also employ sub-controllers of the Kubernetes &lt;em&gt;Controller&lt;/em&gt; component in the control plane to make sure that the current state of your objects is equal to the desired state. The Kubernetes Controller has sub-controllers for specified objects. For example, &lt;a href="https://kubernetes.io/docs/concepts/workloads/controllers/replicaset/"&gt;&lt;em&gt;ReplicaSet&lt;/em&gt;&lt;/a&gt; is a sub-controller that makes sure the desired Pod count remains consistent. When you combine a Custom Resource with a Controller, you get a true &lt;em&gt;declarative API&lt;/em&gt; that allows you to specify the desired state of your resources. &lt;/p&gt;

&lt;p&gt;In this step, you will create a Custom Resource and related objects.&lt;/p&gt;

&lt;p&gt;To create a Custom Resource, first make a file called &lt;code&gt;crd.yml&lt;/code&gt; in the &lt;code&gt;~/k8s-cicd-webinars/webinar1/2-kubernetes/1-Terrafrom/&lt;/code&gt; directory: &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nano crd.yml
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add the following Custom Resource Definition (CRD): &lt;/p&gt;
&lt;div class="code-label " title="~/k8s-cicd-webinars/webinar1/2-kubernetes/1-Terrafrom/crd.yml"&gt;~/k8s-cicd-webinars/webinar1/2-kubernetes/1-Terrafrom/crd.yml&lt;/div&gt;&lt;pre class="code-pre yml"&gt;&lt;code langs=""&gt;apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: webinars.digitalocean.com
spec:
  group: digitalocean.com
  version: v1
  scope: Namespaced
  names:
    plural: webinars
    singular: webinar
    kind: Webinar
    shortNames:
    - wb
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To deploy the CRD defined in &lt;code&gt;crd.yml&lt;/code&gt;, run the following command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl create -f crd.yml 
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will see a confirmation that the resource has been created:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;customresourcedefinition.apiextensions.k8s.io/webinars.digitalocean.com created
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;crd.yml&lt;/code&gt; file has created a new RESTful resource path: &lt;code&gt;/apis/digtialocean.com/v1/namespaces/*/webinars&lt;/code&gt;. You can now refer to your objects using &lt;code&gt;webinars&lt;/code&gt;, &lt;code&gt;webinar&lt;/code&gt;, &lt;code&gt;Webinar&lt;/code&gt;, and &lt;code&gt;wb&lt;/code&gt;, as you listed them in the &lt;code&gt;names&lt;/code&gt; section of the &lt;code&gt;CustomResourceDefinition&lt;/code&gt;. You can check the RESTful resource with the following command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl proxy &amp;amp; curl 127.0.0.1:8001/apis/digitalocean.com
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;span class='note'&gt;&lt;p&gt;
&lt;strong&gt;Note:&lt;/strong&gt; If you followed the initial server setup guide in the prerequisites, then you will need to allow traffic to port &lt;code&gt;8001&lt;/code&gt; in order for this test to work. Enable traffic to this port with the following command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw allow 8001
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;/p&gt;&lt;/span&gt;

&lt;p&gt;You will see the following output: &lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;HTTP/1.1 200 OK
Content-Length: 238
Content-Type: application/json
Date: Fri, 03 Aug 2018 06:10:12 GMT

{
    "apiVersion": "v1", 
    "kind": "APIGroup", 
    "name": "digitalocean.com", 
    "preferredVersion": {
        "groupVersion": "digitalocean.com/v1", 
        "version": "v1"
    }, 
    "serverAddressByClientCIDRs": null, 
    "versions": [
        {
            "groupVersion": "digitalocean.com/v1", 
            "version": "v1"
        }
    ]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, create the object for using new Custom Resources by opening a file called &lt;code&gt;webinar.yml&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nano webinar.yml
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add the following content to create the object: &lt;/p&gt;
&lt;div class="code-label " title="~/k8s-cicd-webinars/webinar1/2-kubernetes/1-Terrafrom/webinar.yml"&gt;~/k8s-cicd-webinars/webinar1/2-kubernetes/1-Terrafrom/webinar.yml&lt;/div&gt;&lt;pre class="code-pre yml"&gt;&lt;code langs=""&gt;apiVersion: "digitalocean.com/v1"
kind: Webinar
metadata:
  name: webinar1
spec:
  name: webinar
  image: nginx
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Run the following command to push these changes to the cluster:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl apply -f webinar.yml 
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will see the following output:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;webinar.digitalocean.com/webinar1 created
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can now manage your &lt;code&gt;webinar&lt;/code&gt; objects using &lt;code&gt;kubectl&lt;/code&gt;. For example:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl get webinar
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;NAME       CREATED AT
webinar1   21s
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You now have an object called &lt;code&gt;webinar1&lt;/code&gt;. If there had been a Controller, it would have intercepted the object creation and performed any defined operations. &lt;/p&gt;

&lt;h3 id="deleting-a-custom-resource-definition"&gt;Deleting a Custom Resource Definition&lt;/h3&gt;

&lt;p&gt;To delete all of the objects for your Custom Resource, use the following command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl delete webinar --all
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will see:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;webinar.digitalocean.com "webinar1" deleted
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Remove the Custom Resource itself:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl delete crd webinars.digitalocean.com
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will see a confirmation that it has been deleted:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;customresourcedefinition.apiextensions.k8s.io "webinars.digitalocean.com" deleted
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After deletion you will not have access to the API endpoint that you tested earlier with the &lt;code&gt;curl&lt;/code&gt; command. &lt;/p&gt;

&lt;p&gt;This sequence is an introduction to how you can extend Kubernetes functionalities without modifying your Kubernetes code. &lt;/p&gt;

&lt;h2 id="step-6-—-deleting-the-kubernetes-cluster"&gt;Step 6 — Deleting the Kubernetes Cluster&lt;/h2&gt;

&lt;p&gt;To destroy the Kubernetes cluster itself, you can use the &lt;code&gt;destroy.sh&lt;/code&gt; script from the &lt;code&gt;~/k8s-cicd-webinars/webinar1/2-kubernetes/1-Terrafrom&lt;/code&gt; folder. Make sure that you are in this directory:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cd ~/k8s-cicd-webinars/webinar1/2-kubernetes/1-Terrafrom
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Run the script: &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;./destroy.sh
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By running this script, you'll allow Terraform to communicate with the DigitalOcean API and delete the servers in your cluster. &lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;In this tutorial, you used different tools to create container images. With these images, you can create containers in any environment. You also set up a Kubernetes cluster using Terraform, and created Deployment and Service objects to deploy and expose your application. Additionally, you extended Kubernetes' functionality by defining a Custom Resource. &lt;/p&gt;

&lt;p&gt;You now have a solid foundation to build a CI/CD environment on Kubernetes, which we'll explore in future articles.  &lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-install-and-secure-openfaas-using-docker-swarm-on-ubuntu-16-04</id>
    <published>2018-09-12T15:47:40Z</published>
    <updated>2018-09-20T19:10:15Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-install-and-secure-openfaas-using-docker-swarm-on-ubuntu-16-04"/>
    <title>How To Install and Secure OpenFaaS Using Docker Swarm on Ubuntu 16.04</title>
    <content type="html">&lt;p&gt;&lt;em&gt;The author selected the &lt;a href="https://www.brightfunds.org/funds/diversity-in-tech"&gt;Diversity in Tech Fund&lt;/a&gt;  to receive a donation as part of the &lt;a href="https://do.co/w4do-cta"&gt;Write for DOnations&lt;/a&gt; program.&lt;/em&gt;&lt;/p&gt;

&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;Serverless architecture hides server instances from the developer and usually exposes an API that allows developers to run their applications in the cloud. This approach helps developers deploy applications quickly, as they can leave provisioning and maintaining instances to the appropriate DevOps teams. It also reduces infrastructure costs, since with the appropriate tooling you can scale your instances per demand. &lt;/p&gt;

&lt;p&gt;Applications that run on serverless platforms are called &lt;em&gt;serverless functions&lt;/em&gt;. A function is containerized, executable code that's used to perform specific operations. Containerizing applications ensures that you can reproduce a consistent environment on many machines, enabling updating and scaling.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.openfaas.com/"&gt;OpenFaaS&lt;/a&gt; is a free and open-source framework for building and hosting serverless functions. With official support for both &lt;a href="https://docs.docker.com/engine/swarm/"&gt;Docker Swarm&lt;/a&gt; and &lt;a href="https://kubernetes.io/"&gt;Kubernetes&lt;/a&gt;, it lets you deploy your applications using the powerful API, command-line interface, or Web UI. It comes with built-in metrics provided by &lt;a href="https://prometheus.io"&gt;Prometheus&lt;/a&gt; and supports auto-scaling on demand, as well as scaling from zero.&lt;/p&gt;

&lt;p&gt;In this tutorial, you'll set up and use OpenFaaS with Docker Swarm running on Ubuntu 16.04, and secure its Web UI and API by setting up &lt;a href="https://traefik.io/"&gt;Traefik&lt;/a&gt; with &lt;a href="https://letsencrypt.org/"&gt;Let's Encypt&lt;/a&gt;. This ensures secure communication between nodes in the cluster, as well as between OpenFaaS and its operators.&lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;To follow this tutorial, you'll need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ubuntu 16.04 running on your local machine. You can use other distributions and operating systems, but make sure you use the appropriate OpenFaaS scripts for your operating system and install all of the dependencies listed in these prerequisites. &lt;/li&gt;
&lt;li&gt;&lt;code&gt;git&lt;/code&gt;, &lt;code&gt;curl&lt;/code&gt;, and &lt;code&gt;jq&lt;/code&gt; installed on your local machine. You'll use &lt;code&gt;git&lt;/code&gt; to clone the OpenFaaS repository, &lt;code&gt;curl&lt;/code&gt; to test the API, and &lt;code&gt;jq&lt;/code&gt; to transform raw &lt;a href="https://www.digitalocean.com/community/tutorials/an-introduction-to-json"&gt;JSON&lt;/a&gt; responses from the API to human-readable JSON. To install the required dependencies for this setup, use the following commands: &lt;code&gt;sudo apt-get update &amp;amp;&amp;amp; sudo apt-get install git curl jq&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;Docker installed, following Steps 1 and 2 of &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-and-use-docker-on-ubuntu-16-04"&gt;How To Install and Use Docker on Ubuntu 16.04&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://hub.docker.com"&gt;Docker Hub&lt;/a&gt; account. To deploy functions to OpenFaaS, they will need to be published on a public container registry. We'll use Docker Hub for this tutorial, since it's both free and widely used. Be sure to authenticate with Docker on your local machine by using the &lt;code&gt;docker login&lt;/code&gt; command. &lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.docker.com/machine/"&gt;Docker Machine&lt;/a&gt; installed, following &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-provision-and-manage-remote-docker-hosts-with-docker-machine-on-ubuntu-16-04"&gt;How To Provision and Manage Remote Docker Hosts with Docker Machine on Ubuntu 16.04&lt;/a&gt;. &lt;/li&gt;
&lt;li&gt;A DigitalOcean personal access token. To create a token, follow &lt;a href="https://www.digitalocean.com/docs/api/create-personal-access-token/"&gt;these instructions&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;A Docker Swarm cluster of 3 nodes, provisioned by following &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-create-a-cluster-of-docker-containers-with-docker-swarm-and-digitalocean-on-ubuntu-16-04"&gt;How to Create a Cluster of Docker Containers with Docker Swarm and DigitalOcean on Ubuntu 16.04&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;A fully registered domain name with an A record pointing to one of the instances in the Docker Swarm. Throughout the tutorial, you'll see &lt;strong&gt;example.com&lt;/strong&gt; as an example domain. You should replace this with your own domain, which you can either purchase on &lt;a href="https://namecheap.com/"&gt;Namecheap&lt;/a&gt;, or get for free on &lt;a href="http://www.freenom.com/en/index.html"&gt;Freenom&lt;/a&gt;. You can also use a different domain registrar of your choice.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="step-1-—-downloading-openfaas-and-installing-the-openfaas-cli"&gt;Step 1 — Downloading OpenFaaS and Installing the OpenFaaS CLI&lt;/h2&gt;

&lt;p&gt;To deploy OpenFaaS to your Docker Swarm, you will need to download the deployment manifests and scripts. The easiest way to obtain them is to clone the official OpenFaas repository and check out the appropriate tag, which represents an OpenFaaS release.&lt;/p&gt;

&lt;p&gt;In addition to cloning the repository, you'll also install the FaaS CLI, a powerful command-line utility that you can use to manage and deploy new functions from your terminal. It provides templates for creating your own functions in most major programming languages. In &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-and-secure-openfaas-using-docker-swarm-on-ubuntu-16-04#step-7-%E2%80%94-creating-functions-with-the-faas-cli"&gt;Step 7&lt;/a&gt;, you'll use it to create a &lt;a href="https://www.digitalocean.com/community/tags/python"&gt;Python&lt;/a&gt; function and deploy it on OpenFaaS.&lt;/p&gt;

&lt;p&gt;For this tutorial, you'll deploy OpenFaaS v&lt;span class="highlight"&gt;0.8.9&lt;/span&gt;. While the steps for deploying other versions should be similar, make sure to check out the &lt;a href="https://github.com/openfaas/faas-cli/blob/master/CHANGELOG.md"&gt;project changelog&lt;/a&gt; to ensure there are no breaking changes.&lt;/p&gt;

&lt;p&gt;First, navigate to your home directory and run the following command to clone the repository to the &lt;code&gt;~/faas&lt;/code&gt; directory:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cd ~
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;git clone https://github.com/openfaas/faas.git
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Navigate to the newly-created &lt;code&gt;~/faas&lt;/code&gt; directory:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cd ~/faas
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When you clone the repository, you'll get files from the master branch that contain the latest changes. Because breaking changes can get into the master branch, it's not recommended for use in production. Instead, let's check out the &lt;code&gt;&lt;span class="highlight"&gt;0.8.9&lt;/span&gt;&lt;/code&gt; tag:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;git checkout &lt;span class="highlight"&gt;0.8.9&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The output contains a message about the successful checkout and a warning about committing changes to this branch: &lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Note: checking out '&lt;span class="highlight"&gt;0.8.9&lt;/span&gt;'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:

  git checkout -b &amp;lt;new-branch-name&amp;gt;

HEAD is now at 8f0d2d1 Expose scale-function endpoint
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you see any errors, make sure to resolve them by following the on-screen instructions before continuing.&lt;/p&gt;

&lt;p&gt;With the OpenFaaS repository downloaded, complete with the necessary manifest files, let's proceed to installing the FaaS CLI.&lt;/p&gt;

&lt;p&gt;The easiest way to install the FaaS CLI is to use the official script. In your terminal, navigate to your home directory and download the script using the following command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cd ~
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;curl -sSL -o faas-cli.sh https://cli.openfaas.com
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will download the &lt;code&gt;faas-cli.sh&lt;/code&gt; script to your home directory. Before executing the script, it's a good idea to check the contents:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;less faas-cli.sh
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can exit the preview by pressing &lt;code&gt;q&lt;/code&gt;. Once you have verified content of the script, you can proceed with the installation by giving executable permissions to the script and executing it. Execute the script as root so it will automatically copy to your &lt;code&gt;PATH&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;chmod +x faas-cli.sh
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo ./faas-cli.sh
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The output contains information about the installation progress and the CLI version that you've installed: &lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;x86_64
Downloading package https://github.com/openfaas/faas-cli/releases/download/&lt;span class="highlight"&gt;0.6.17&lt;/span&gt;/faas-cli as /tmp/faas-cli
Download complete.

Running as root - Attempting to move faas-cli to /usr/local/bin
New version of faas-cli installed to /usr/local/bin
Creating alias 'faas' for 'faas-cli'.
  ___                   _____           ____
 / _ \ _ __   ___ _ __ |  ___|_ _  __ _/ ___|
| | | | '_ \ / _ \ '_ \| |_ / _` |/ _` \___ \
| |_| | |_) |  __/ | | |  _| (_| | (_| |___) |
 \___/| .__/ \___|_| |_|_|  \__,_|\__,_|____/
      |_|

CLI:
 commit:  &lt;span class="highlight"&gt;b5597294da6dd98457434fafe39054c993a5f7e7&lt;/span&gt;
 version: &lt;span class="highlight"&gt;0.6.17&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you see an error, make sure to resolve it by following the on-screen instructions before continuing with the tutorial.&lt;/p&gt;

&lt;p&gt;At this point, you have the FaaS CLI installed. To learn more about commands you can use, execute the CLI without any arguments:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;faas-cli
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The output shows available commands and flags:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;  ___                   _____           ____
 / _ \ _ __   ___ _ __ |  ___|_ _  __ _/ ___|
| | | | '_ \ / _ \ '_ \| |_ / _` |/ _` \___ \
| |_| | |_) |  __/ | | |  _| (_| | (_| |___) |
 \___/| .__/ \___|_| |_|_|  \__,_|\__,_|____/
      |_|


Manage your OpenFaaS functions from the command line

Usage:
  faas-cli [flags]
  faas-cli [command]

Available Commands:
  build          Builds OpenFaaS function containers
  cloud          OpenFaaS Cloud commands
  deploy         Deploy OpenFaaS functions
  help           Help about any command
  invoke         Invoke an OpenFaaS function
  list           List OpenFaaS functions
  login          Log in to OpenFaaS gateway
  logout         Log out from OpenFaaS gateway
  new            Create a new template in the current folder with the name given as name
  push           Push OpenFaaS functions to remote registry (Docker Hub)
  remove         Remove deployed OpenFaaS functions
  store          OpenFaaS store commands
  template       Downloads templates from the specified github repo
  version        Display the clients version information

Flags:
      --filter string   Wildcard to match with function names in YAML file
  -h, --help            help for faas-cli
      --regex string    Regex to match with function names in YAML file
  -f, --yaml string     Path to YAML file describing function(s)

Use "faas-cli [command] --help" for more information about a command.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You have now successfully obtained the OpenFaaS manifests and installed the FaaS CLI, which you can use to manage your OpenFaaS instance from your terminal.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;~/faas&lt;/code&gt; directory contains files from the &lt;code&gt;&lt;span class="highlight"&gt;0.8.9&lt;/span&gt;&lt;/code&gt; release, which means you can now deploy OpenFaaS to your Docker Swarm. Before doing so, let's modify the deployment manifest file to include Traefik, which will secure your OpenFaaS setup by setting up Let's Encrypt.&lt;/p&gt;

&lt;h2 id="step-2-—-configuring-traefik"&gt;Step 2 — Configuring Traefik&lt;/h2&gt;

&lt;p&gt;Traefik is a Docker-aware reverse proxy that comes with SSL support provided by Let's Encrypt. SSL protocol ensures that you communicate with the Swarm cluster securely by encrypting the data you send and receive between nodes.&lt;/p&gt;

&lt;p&gt;To use Traefik with OpenFaaS, you need to modify the OpenFaaS deployment manifest to include Traefik and tell OpenFaaS to use Traefik instead of directly exposing its services to the internet.&lt;/p&gt;

&lt;p&gt;Navigate back to the &lt;code&gt;~/faas&lt;/code&gt; directory and open the OpenFaaS deployment manifest in a text editor:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cd ~/faas
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;nano ~/faas/docker-compose.yml
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span class='note'&gt;&lt;strong&gt;Note:&lt;/strong&gt; The Docker Compose manifest file uses &lt;a href="http://www.yaml.org/start.html"&gt;YAML formatting&lt;/a&gt;, which strictly forbids tabs and requires two spaces for indentation. The manifest will fail to deploy if the file is incorrectly formatted.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;The OpenFaaS deployment is comprised of several services, defined under the &lt;code&gt;services&lt;/code&gt; directive, that provide the dependencies needed to run OpenFaaS, the OpenFaaS API and Web UI, and Prometheus and AlertManager (for handling metrics).&lt;/p&gt;

&lt;p&gt;At the beginning of the &lt;code&gt;services&lt;/code&gt; section, add a new service called &lt;code&gt;traefik&lt;/code&gt;, which uses the &lt;code&gt;traefik:v1.6&lt;/code&gt; image for the deployment:&lt;/p&gt;
&lt;div class="code-label " title="~/faas/docker-compose.yml"&gt;~/faas/docker-compose.yml&lt;/div&gt;&lt;pre class="code-pre yaml"&gt;&lt;code langs=""&gt;version: "3.3"
services:
    &lt;span class="highlight"&gt;traefik:&lt;/span&gt;
        &lt;span class="highlight"&gt;image: traefik:v1.6&lt;/span&gt;
    gateway:
         ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The Traefik image is coming from the &lt;a href="https://hub.docker.com/_/traefik/"&gt;Traefik Docker Hub repository&lt;/a&gt;, where you can find a list of all available images.&lt;/p&gt;

&lt;p&gt;Next, let's instruct Docker to run Traefik using the &lt;code&gt;command&lt;/code&gt; directive. This will run Traefik, configure it to work with Docker Swarm, and provide SSL using Let's Encrypt. The following flags will configure Traefik:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;--docker.*&lt;/code&gt;: These flags tell Traefik to use Docker and specify that it's running in a Docker Swarm cluster.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--web=true&lt;/code&gt;: This flag enables Traefik's Web UI.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--defaultEntryPoints&lt;/code&gt; and &lt;code&gt;--entryPoints&lt;/code&gt;: These flags define entry points and protocols to be used. In our case this includes HTTP on port &lt;code&gt;80&lt;/code&gt; and HTTPS on port &lt;code&gt;443&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--acme.*&lt;/code&gt;: These flags tell Traefik to use &lt;a href="https://letsencrypt.org/docs/client-options/"&gt;ACME&lt;/a&gt; to generate Let's Encrypt certificates to secure your OpenFaaS cluster with SSL.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Make sure to replace the &lt;code&gt;&lt;span class="highlight"&gt;example.com&lt;/span&gt;&lt;/code&gt; domain placeholders in the &lt;code&gt;--acme.domains&lt;/code&gt; and &lt;code&gt;--acme.email&lt;/code&gt; flags with the domain you're going to use to access OpenFaaS. You can specify multiple domains by separating them with a comma and space. The email address is for SSL notifications and alerts, including certificate expiry alerts. In this case, Traefik will handle renewing certificates automatically, so you can ignore expiry alerts. &lt;/p&gt;

&lt;p&gt;Add the following block of code below the &lt;code&gt;image&lt;/code&gt; directive, and above &lt;code&gt;gateway&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-label " title="~/faas/docker-compose.yml"&gt;~/faas/docker-compose.yml&lt;/div&gt;&lt;pre class="code-pre yaml"&gt;&lt;code langs=""&gt;...
    traefik:
        image: traefik:v1.6
        command: -c --docker=true
            --docker.swarmmode=true
            --docker.domain=traefik
            --docker.watch=true
            --web=true
            --defaultEntryPoints='http,https'
            --entryPoints='Name:https Address::443 TLS'
            --entryPoints='Name:http Address::80'
            --acme=true
            --acme.entrypoint='https'
            --acme.httpchallenge=true
            --acme.httpchallenge.entrypoint='http'
            --acme.domains='&lt;span class="highlight"&gt;example.com&lt;/span&gt;, &lt;span class="highlight"&gt;www.example.com&lt;/span&gt;'
            --acme.email='&lt;span class="highlight"&gt;sammy@example.com&lt;/span&gt;'
            --acme.ondemand=true
            --acme.onhostrule=true
            --acme.storage=/etc/traefik/acme/acme.json
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With the &lt;code&gt;command&lt;/code&gt; directive in place, let's tell Traefik what ports to expose to the internet. Traefik uses port &lt;code&gt;8080&lt;/code&gt; for its operations, while OpenFaaS will use port &lt;code&gt;80&lt;/code&gt; for non-secure communication and port &lt;code&gt;443&lt;/code&gt; for secure communication.&lt;/p&gt;

&lt;p&gt;Add the following &lt;code&gt;ports&lt;/code&gt; directive below the &lt;code&gt;command&lt;/code&gt; directive. The &lt;code&gt;&lt;span class="highlight"&gt;port-internet&lt;/span&gt;:&lt;span class="highlight"&gt;port-docker&lt;/span&gt;&lt;/code&gt; notation ensures that the port on the left side is exposed by Traefik to the internet and maps to the container's port on the right side:&lt;/p&gt;
&lt;div class="code-label " title="~/faas/docker-compose.yml"&gt;~/faas/docker-compose.yml&lt;/div&gt;&lt;pre class="code-pre yaml"&gt;&lt;code langs=""&gt;        ...
        command:
            ...
        &lt;span class="highlight"&gt;ports:&lt;/span&gt;
            - &lt;span class="highlight"&gt;80:80&lt;/span&gt;
            - &lt;span class="highlight"&gt;8080:8080&lt;/span&gt;
            - &lt;span class="highlight"&gt;443:443&lt;/span&gt;
        ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, using the &lt;code&gt;volumes&lt;/code&gt; directive, mount the Docker socket file from the host running Docker to Traefik. The Docker socket file communicates with the Docker API in order to manage your containers and get details about them, such as number of containers and their IP addresses. You will also mount the volume called &lt;code&gt;acme&lt;/code&gt;, which we'll define later in this step.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;networks&lt;/code&gt; directive instructs Traefik to use the &lt;code&gt;functions&lt;/code&gt; network, which is deployed along with OpenFaaS. This network ensures that functions can communicate with other parts of the system, including the API.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;deploy&lt;/code&gt; directive instructs Docker to run Traefik only on the Docker Swarm manager node.&lt;/p&gt;

&lt;p&gt;Add the following directives below the &lt;code&gt;ports&lt;/code&gt; directive:&lt;/p&gt;
&lt;div class="code-label " title="~/faas/docker-compose.yml"&gt;~/faas/docker-compose.yml&lt;/div&gt;&lt;pre class="code-pre yaml"&gt;&lt;code langs=""&gt;        ...
        volumes:
            - "/var/run/docker.sock:/var/run/docker.sock"
            - "acme:/etc/traefik/acme"
        networks:
            - functions
        deploy:
            placement:
                constraints: [node.role == manager]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At this point, the &lt;code&gt;traefik&lt;/code&gt; service block should look like this:&lt;/p&gt;
&lt;div class="code-label " title="~/faas/docker-compose.yml"&gt;~/faas/docker-compose.yml&lt;/div&gt;&lt;pre class="code-pre yaml"&gt;&lt;code langs=""&gt;version: "3.3"
services:
    traefik:
        image: traefik:v1.6
        command: -c --docker=true
            --docker.swarmmode=true
            --docker.domain=traefik
            --docker.watch=true
            --web=true
            --defaultEntryPoints='http,https'
            --entryPoints='Name:https Address::443 TLS'
            --entryPoints='Name:http Address::80'            
            --acme=true
            --acme.entrypoint='https'
            --acme.httpchallenge=true
            --acme.httpchallenge.entrypoint='http'
            --acme.domains='&lt;span class="highlight"&gt;example.com&lt;/span&gt;, &lt;span class="highlight"&gt;www.example.com&lt;/span&gt;'
            --acme.email='&lt;span class="highlight"&gt;sammy@example.com&lt;/span&gt;'
            --acme.ondemand=true
            --acme.onhostrule=true
            --acme.storage=/etc/traefik/acme/acme.json
        ports:
            - 80:80
            - 8080:8080
            - 443:443
        volumes:
            - "/var/run/docker.sock:/var/run/docker.sock"
            - "acme:/etc/traefik/acme"
        networks:
          - functions
        deploy:
          placement:
            constraints: [node.role == manager]

    gateway:
        ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;While this configuration ensures that Traefik will be deployed with OpenFaaS, you also need to configure OpenFaaS to work with Traefik. By default, the &lt;code&gt;gateway&lt;/code&gt; service is configured to run on port &lt;code&gt;8080&lt;/code&gt;, which overlaps with Traefik.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;gateway&lt;/code&gt; service provides the API gateway you can use to deploy, run, and manage your functions. It handles metrics (via Prometheus) and auto-scaling, and hosts the Web UI.&lt;/p&gt;

&lt;p&gt;Our goal is to expose the &lt;code&gt;gateway&lt;/code&gt; service using Traefik instead of exposing it directly to the internet.&lt;/p&gt;

&lt;p&gt;Locate the &lt;code&gt;gateway&lt;/code&gt; service, which should look like this:&lt;/p&gt;
&lt;div class="code-label " title="~/faas/docker-compose.yml"&gt;~/faas/docker-compose.yml&lt;/div&gt;&lt;pre class="code-pre yaml"&gt;&lt;code langs=""&gt;...
    gateway:
        ports:
            - 8080:8080
        image: openfaas/gateway:0.8.7
        networks:
            - functions
        environment:
            functions_provider_url: "http://faas-swarm:8080/"
            read_timeout:  "300s"        # Maximum time to read HTTP request
            write_timeout: "300s"        # Maximum time to write HTTP response
            upstream_timeout: "300s"     # Maximum duration of upstream function call - should be more than read_timeout and write_timeout
            dnsrr: "true"               # Temporarily use dnsrr in place of VIP while issue persists on PWD
            faas_nats_address: "nats"
            faas_nats_port: 4222
            direct_functions: "true"    # Functions are invoked directly over the overlay network
            direct_functions_suffix: ""
            basic_auth: "${BASIC_AUTH:-true}"
            secret_mount_path: "/run/secrets/"
            scale_from_zero: "false"
        deploy:
            resources:
                # limits:   # Enable if you want to limit memory usage
                #     memory: 200M
                reservations:
                    memory: 100M
            restart_policy:
                condition: on-failure
                delay: 5s
                max_attempts: 20
                window: 380s
            placement:
                constraints:
                    - 'node.platform.os == linux'
        secrets:
            - basic-auth-user
            - basic-auth-password
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Remove the &lt;code&gt;ports&lt;/code&gt; directive from the service to avoid exposing the &lt;code&gt;gateway&lt;/code&gt; service directly.&lt;/p&gt;

&lt;p&gt;Next, add the following &lt;code&gt;lables&lt;/code&gt; directive to the &lt;code&gt;deploy&lt;/code&gt; section of the &lt;code&gt;gateway&lt;/code&gt; service. This directive exposes the &lt;code&gt;/ui&lt;/code&gt;, &lt;code&gt;/system&lt;/code&gt;, and &lt;code&gt;/function&lt;/code&gt; endpoints on port &lt;code&gt;8080&lt;/code&gt; over Traefik:&lt;/p&gt;
&lt;div class="code-label " title="~/faas/docker-compose.yml"&gt;~/faas/docker-compose.yml&lt;/div&gt;&lt;pre class="code-pre yaml"&gt;&lt;code langs=""&gt;        ...
        deploy:
            &lt;span class="highlight"&gt;labels:&lt;/span&gt;
                - &lt;span class="highlight"&gt;traefik.port=8080&lt;/span&gt;
                - &lt;span class="highlight"&gt;traefik.frontend.rule=PathPrefix:/ui,/system,/function&lt;/span&gt;
            resources:
            ...            
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;/ui&lt;/code&gt; endpoint exposes the OpenFaaS Web UI, which is covered in the &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-and-secure-openfaas-using-docker-swarm-on-ubuntu-16-04#step-6-%E2%80%94-using-the-openfaas-web-ui"&gt;Step 6&lt;/a&gt; of this tutorial. The &lt;code&gt;/system&lt;/code&gt; endpoint is the API endpoint used to manage OpenFaaS, while the &lt;code&gt;/function&lt;/code&gt; endpoint exposes the API endpoints for managing and running functions. &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-and-secure-openfaas-using-docker-swarm-on-ubuntu-16-04#step-5-%E2%80%94-using-the-openfaas-api"&gt;Step 5&lt;/a&gt; of this tutorial covers the OpenFaaS API in detail.&lt;/p&gt;

&lt;p&gt;After modifications, your &lt;code&gt;gateway&lt;/code&gt; service should look like this:&lt;/p&gt;
&lt;div class="code-label " title="~/faas/docker-compose.yml"&gt;~/faas/docker-compose.yml&lt;/div&gt;&lt;pre class="code-pre yaml"&gt;&lt;code langs=""&gt;...
    gateway:       
        image: openfaas/gateway:0.8.7
        networks:
            - functions
        environment:
            functions_provider_url: "http://faas-swarm:8080/"
            read_timeout:  "300s"        # Maximum time to read HTTP request
            write_timeout: "300s"        # Maximum time to write HTTP response
            upstream_timeout: "300s"     # Maximum duration of upstream function call - should be more than read_timeout and write_timeout
            dnsrr: "true"               # Temporarily use dnsrr in place of VIP while issue persists on PWD
            faas_nats_address: "nats"
            faas_nats_port: 4222
            direct_functions: "true"    # Functions are invoked directly over the overlay network
            direct_functions_suffix: ""
            basic_auth: "${BASIC_AUTH:-true}"
            secret_mount_path: "/run/secrets/"
            scale_from_zero: "false"
        deploy:
            labels:
                - traefik.port=8080
                - traefik.frontend.rule=PathPrefix:/ui,/system,/function
            resources:
                # limits:   # Enable if you want to limit memory usage
                #     memory: 200M
                reservations:
                    memory: 100M
            restart_policy:
                condition: on-failure
                delay: 5s
                max_attempts: 20
                window: 380s
            placement:
                constraints:
                    - 'node.platform.os == linux'
        secrets:
            - basic-auth-user
            - basic-auth-password
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, let's define the &lt;code&gt;acme&lt;/code&gt; volume used for storing Let's Encrypt certificates. We can define an empty volume, meaning data will not persist if you destroy the container. If you destroy the container, the certificates will be regenerated the next time you start Traefik. &lt;/p&gt;

&lt;p&gt;Add the following &lt;code&gt;volumes&lt;/code&gt; directive on the last line of the file:&lt;/p&gt;
&lt;div class="code-label " title="~/faas/docker-compose.yml"&gt;~/faas/docker-compose.yml&lt;/div&gt;&lt;pre class="code-pre yaml"&gt;&lt;code langs=""&gt;...
volumes:
    acme:
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once you're done, save the file and close your text editor. At this point, you've configured Traefik to protect your OpenFaaS deployment and Docker Swarm. Now you're ready to deploy it along with OpenFaaS on your Swarm cluster.&lt;/p&gt;

&lt;h2 id="step-3-—-deploying-openfaas"&gt;Step 3 — Deploying OpenFaaS&lt;/h2&gt;

&lt;p&gt;Now that you have prepared the OpenFaaS deployment manifest, you're ready to deploy it and start using OpenFaaS. To deploy, you'll use the &lt;code&gt;deploy_stack.sh&lt;/code&gt; script. This script is meant to be used on Linux and macOS operating systems, but in the OpenFaaS directory you can also find appropriate scripts for &lt;a href="https://github.com/openfaas/faas/blob/master/deploy_stack.ps1"&gt;Windows&lt;/a&gt; and &lt;a href="https://github.com/openfaas/faas/blob/master/deploy_stack.ps1"&gt;ARM systems&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Before deploying OpenFaaS, you will need to instruct &lt;code&gt;docker-machine&lt;/code&gt; to execute Docker commands from the script on one of the machines in the Swarm. For this tutorial, let's use the &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-create-a-cluster-of-docker-containers-with-docker-swarm-and-digitalocean-on-ubuntu-16-04#step-3-%E2%80%94-initializing-the-cluster-manager"&gt;Swarm manager&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you have the &lt;code&gt;docker-machine use&lt;/code&gt; command configured, you can use it:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;docker-machine use &lt;span class="highlight"&gt;node-1&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If not, use the following command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;eval $(docker-machine env &lt;span class="highlight"&gt;node-1&lt;/span&gt;)
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;deploy_stack.sh&lt;/code&gt; script deploys all of the resources required for OpenFaaS to work as expected, including configuration files, network settings, services, and credentials for authorization with the OpenFaaS server.&lt;/p&gt;

&lt;p&gt;Let's execute the script, which will take several minutes to finish deploying:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;~/faas/deploy_stack.sh
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The output shows a list of resources that are created in the deployment process, as well as the credentials you will use to access the OpenFaaS server and the FaaS CLI command. &lt;/p&gt;

&lt;p&gt;Write down these credentials, as you will need them throughout the tutorial to access the Web UI and the API:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Attempting to create credentials for gateway..
roozmk0y1jkn17372a8v9y63g
q1odtpij3pbqrmmf8msy3ampl
[Credentials]
 username: &lt;span class="highlight"&gt;admin&lt;/span&gt;
 password: &lt;span class="highlight"&gt;your_openfaas_password&lt;/span&gt;
 echo -n &lt;span class="highlight"&gt;your_openfaas_password&lt;/span&gt; | faas-cli login --username=&lt;span class="highlight"&gt;admin&lt;/span&gt; --password-stdin

Enabling basic authentication for gateway..

Deploying OpenFaaS core services
Creating network func_functions
Creating config func_alertmanager_config
Creating config func_prometheus_config
Creating config func_prometheus_rules
Creating service func_alertmanager
Creating service func_traefik
Creating service func_gateway
Creating service func_faas-swarm
Creating service func_nats
Creating service func_queue-worker
Creating service func_prometheus
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you see any errors, follow the on-screen instructions to resolve them before continuing the tutorial.&lt;/p&gt;

&lt;p&gt;Before continuing, let's authenticate the FaaS CLI with the OpenFaaS server using the command provided by the deployment script.&lt;/p&gt;

&lt;p&gt;The script outputted the flags you need to provide to the command, but you will need to add an additional flag, &lt;code&gt;--gateway&lt;/code&gt;, with the address of your OpenFaaS server, as the FaaS CLI assumes the gateway server is running on &lt;code&gt;localhost&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;echo -n &lt;span class="highlight"&gt;your_openfaas_password&lt;/span&gt; | faas-cli login --username=&lt;span class="highlight"&gt;admin&lt;/span&gt; --password-stdin --gateway &lt;span class="highlight"&gt;https://example.com&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The output contains a message about successful authorization:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Calling the OpenFaaS server to validate the credentials...
credentials saved for admin https://example.com
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At this point, you have a fully-functional OpenFaaS server deployed on your Docker Swarm cluster, as well as the FaaS CLI configured to use your newly deployed server. Before testing how to use OpenFaaS, let's deploy some sample functions to get started.&lt;/p&gt;

&lt;h2 id="step-4-—-deploying-openfaas-sample-functions"&gt;Step 4 — Deploying OpenFaaS Sample Functions&lt;/h2&gt;

&lt;p&gt;Initially, OpenFaaS comes without any functions deployed. To start testing and using it, you will need some functions.&lt;/p&gt;

&lt;p&gt;The OpenFaaS project hosts some sample functions, and you can find a list of &lt;a href="https://github.com/openfaas/faas/tree/master/sample-functions"&gt;available functions along with their deployment manifests in the OpenFaaS repository&lt;/a&gt;. Some of the sample functions include &lt;code&gt;nodeinfo&lt;/code&gt;, for showing information about the node where a function is running, &lt;code&gt;wordcount&lt;/code&gt;, for counting the number of words in a passed request, and &lt;code&gt;markdown&lt;/code&gt;, for converting passed markdown input to HTML output. &lt;/p&gt;

&lt;p&gt;The &lt;code&gt;stack.yml&lt;/code&gt; manifest in the &lt;code&gt;~/faas&lt;/code&gt; directory deploys several sample functions along with the functions mentioned above. You can deploy it using the FaaS CLI.&lt;/p&gt;

&lt;p&gt;Run the following &lt;code&gt;faas-cli&lt;/code&gt; command, which takes the path to the stack manifest and the address of your OpenFaaS server:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;faas-cli deploy -f ~/faas/stack.yml --gateway https://&lt;span class="highlight"&gt;example.com&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The output contains status codes and messages indicating whether or not the deployment was successful:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Deploying: wordcount.

Deployed. 200 OK.
URL: https://&lt;span class="highlight"&gt;example.com&lt;/span&gt;/function/wordcount

Deploying: base64.

Deployed. 200 OK.
URL: https://&lt;span class="highlight"&gt;example.com&lt;/span&gt;/function/base64

Deploying: markdown.

Deployed. 200 OK.
URL: https://&lt;span class="highlight"&gt;example.com&lt;/span&gt;/function/markdown

Deploying: hubstats.

Deployed. 200 OK.
URL: https://&lt;span class="highlight"&gt;example.com&lt;/span&gt;/function/hubstats

Deploying: nodeinfo.

Deployed. 200 OK.
URL: https://&lt;span class="highlight"&gt;example.com&lt;/span&gt;/function/nodeinfo

Deploying: echoit.

Deployed. 200 OK.
URL: https://&lt;span class="highlight"&gt;example.com&lt;/span&gt;/function/echoit
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you see any errors, make sure to resolve them by following the on-screen instructions.&lt;/p&gt;

&lt;p&gt;Once the stack deployment is done, list all of the functions to make sure they're deployed and ready to be used:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;faas-cli list --gateway https://&lt;span class="highlight"&gt;example.com&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The output contains a list of functions, along with their replica numbers and an invocations count:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Function                        Invocations     Replicas
markdown                        0               1
wordcount                       0               1
base64                          0               1
nodeinfo                        0               1
hubstats                        0               1
echoit                          0               1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you don't see your functions here, make sure the &lt;code&gt;faas-cli deploy&lt;/code&gt; command executed successfully.&lt;/p&gt;

&lt;p&gt;You can now use the sample OpenFaaS functions to test and demonstrate how to use the API, Web UI, and CLI. In the next step, you'll start by using the OpenFaaS API to list and run functions.&lt;/p&gt;

&lt;h2 id="step-5-—-using-the-openfaas-api"&gt;Step 5 — Using the OpenFaaS API&lt;/h2&gt;

&lt;p&gt;OpenFaaS comes with a powerful API that you can use to manage and execute your serverless functions. Let's use &lt;a href="https://swagger.io/"&gt;Swagger&lt;/a&gt;, a tool for architecting, testing, and documenting APIs, to browse the API documentation, and then use the API to list and run functions. &lt;/p&gt;

&lt;p&gt;With Swagger, you can inspect the API documentation to find out what endpoints are available and how you can use them. In the OpenFaaS repository, you can find the &lt;a href="https://github.com/openfaas/faas/blob/master/api-docs/swagger.yml"&gt;Swagger API specification&lt;/a&gt;, which can be used with the Swagger editor to convert the specification to human-readable form.&lt;/p&gt;

&lt;p&gt;Navigate your web browser to &lt;code&gt;http://editor.swagger.io/&lt;/code&gt;. You should be welcomed with the following screen:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/openfaas_ubuntu_1604/swagger_welcome.png" alt="Swagger Editor Welcome page"&gt;&lt;/p&gt;

&lt;p&gt;Here you'll find a text editor containing the source code for the sample Swagger specification, and the human-readable API documentation on the right.&lt;/p&gt;

&lt;p&gt;Let's import the OpenFaaS Swagger specification. In the top menu, click on the &lt;strong&gt;File&lt;/strong&gt; button, and then on &lt;strong&gt;Import URL&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/openfaas_ubuntu_1604/swagger_editor.png" alt="Swagger Editor Import URL"&gt;&lt;/p&gt;

&lt;p&gt;You'll see a pop-up, where you need to enter the address of the Swagger API specification. If you don't see the pop-up, make sure pop-ups are enabled for your web browser.&lt;/p&gt;

&lt;p&gt;In the field, enter the link to the Swagger OpenFaaS API specification: &lt;code&gt;https://raw.githubusercontent.com/openfaas/faas/master/api-docs/swagger.yml&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/openfaas_ubuntu_1604/swagger_editor_url.png" alt="Swagger Editor Input URL"&gt;&lt;/p&gt;

&lt;p&gt;After clicking on the &lt;strong&gt;OK&lt;/strong&gt; button, the Swagger editor will show you the API reference for OpenFaaS, which should look like this:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/openfaas_ubuntu_1604/swagger_openfaas.png" alt="Swagger Editor OpenFaaS API specification"&gt;&lt;/p&gt;

&lt;p&gt;On the left side you can see the source of the API reference file, while on the right side you can see a list of endpoints, along with short descriptions. Clicking on an endpoint shows you more details about it, including what parameters it takes, what method it uses, and possible responses:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/openfaas_ubuntu_1604/swagger_endpoint_details.png" alt="Swagger Editor Endpoint details"&gt;&lt;/p&gt;

&lt;p&gt;Once you know what endpoints are available and what parameters they expect, you can use them to manage your functions.&lt;/p&gt;

&lt;p&gt;Next, you'll use a &lt;code&gt;curl&lt;/code&gt; command to communicate with the API, so navigate back to your terminal. With the &lt;code&gt;-u&lt;/code&gt; flag, you will be able to pass the &lt;code&gt;&lt;span class="highlight"&gt;admin:your_openfaas_password&lt;/span&gt;&lt;/code&gt; pair that you got in Step 3, while the &lt;code&gt;-X&lt;/code&gt; flag will define the request method. You will also pass your endpoint URL, &lt;code&gt;https://&lt;span class="highlight"&gt;example.com&lt;/span&gt;/system/functions&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;curl -u &lt;span class="highlight"&gt;admin&lt;/span&gt;:&lt;span class="highlight"&gt;your_openfaas_password&lt;/span&gt; -X &lt;span class="highlight"&gt;GET&lt;/span&gt; https://&lt;span class="highlight"&gt;example.com&lt;/span&gt;/system/functions
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can see the required method for each endpoint in the API docs.&lt;/p&gt;

&lt;p&gt;In Step 4, you deployed several sample functions, which should appear in the output:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;[{"name":"base64","image":"functions/alpine:latest","invocationCount":0,"replicas":1,"envProcess":"base64","availableReplicas":0,"labels":{"com.openfaas.function":"base64","function":"true"}},{"name":"nodeinfo","image":"functions/nodeinfo:latest","invocationCount":0,"replicas":1,"envProcess":"","availableReplicas":0,"labels":{"com.openfaas.function":"nodeinfo","function":"true"}},{"name":"hubstats","image":"functions/hubstats:latest","invocationCount":0,"replicas":1,"envProcess":"","availableReplicas":0,"labels":{"com.openfaas.function":"hubstats","function":"true"}},{"name":"markdown","image":"functions/markdown-render:latest","invocationCount":0,"replicas":1,"envProcess":"","availableReplicas":0,"labels":{"com.openfaas.function":"markdown","function":"true"}},{"name":"echoit","image":"functions/alpine:latest","invocationCount":0,"replicas":1,"envProcess":"cat","availableReplicas":0,"labels":{"com.openfaas.function":"echoit","function":"true"}},{"name":"wordcount","image":"functions/alpine:latest","invocationCount":0,"replicas":1,"envProcess":"wc","availableReplicas":0,"labels":{"com.openfaas.function":"wordcount","function":"true"}}]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you don't see output that looks like this, or if you see an error, follow the on-screen instructions to resolve the problem before continuing with the tutorial. Make sure you're sending the request to the correct endpoint using the recommended method and the right credentials. You can also check the logs for the &lt;code&gt;gateway&lt;/code&gt; service using the following command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;docker service logs func_gateway
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By default, the API response to the &lt;code&gt;curl&lt;/code&gt; call returns raw JSON without new lines, which is not human-readable. To parse it, pipe &lt;code&gt;curl&lt;/code&gt;'s response to the &lt;code&gt;jq&lt;/code&gt; utility, which will convert the JSON to human-readable form:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;curl -u &lt;span class="highlight"&gt;admin&lt;/span&gt;:&lt;span class="highlight"&gt;your_openfaas_password&lt;/span&gt; -X &lt;span class="highlight"&gt;GET&lt;/span&gt; https://&lt;span class="highlight"&gt;example.com&lt;/span&gt;/system/functions | jq
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The output is now in human-readable form. You can see the function name, which you can use to manage and invoke functions with the API, the number of invocations, as well as information such as labels and number of replicas, relevant to Docker:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-json"&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;[
  {
    "name": "base64",
    "image": "functions/alpine:latest",
    "invocationCount": 0,
    "replicas": 1,
    "envProcess": "base64",
    "availableReplicas": 0,
    "labels": {
      "com.openfaas.function": "base64",
      "function": "true"
    }
  },
  {
    "name": "nodeinfo",
    "image": "functions/nodeinfo:latest",
    "invocationCount": 0,
    "replicas": 1,
    "envProcess": "",
    "availableReplicas": 0,
    "labels": {
      "com.openfaas.function": "nodeinfo",
      "function": "true"
    }
  },
  {
    "name": "hubstats",
    "image": "functions/hubstats:latest",
    "invocationCount": 0,
    "replicas": 1,
    "envProcess": "",
    "availableReplicas": 0,
    "labels": {
      "com.openfaas.function": "hubstats",
      "function": "true"
    }
  },
  {
    "name": "markdown",
    "image": "functions/markdown-render:latest",
    "invocationCount": 0,
    "replicas": 1,
    "envProcess": "",
    "availableReplicas": 0,
    "labels": {
      "com.openfaas.function": "markdown",
      "function": "true"
    }
  },
  {
    "name": "echoit",
    "image": "functions/alpine:latest",
    "invocationCount": 0,
    "replicas": 1,
    "envProcess": "cat",
    "availableReplicas": 0,
    "labels": {
      "com.openfaas.function": "echoit",
      "function": "true"
    }
  },
  {
    "name": "wordcount",
    "image": "functions/alpine:latest",
    "invocationCount": 0,
    "replicas": 1,
    "envProcess": "wc",
    "availableReplicas": 0,
    "labels": {
      "com.openfaas.function": "wordcount",
      "function": "true"
    }
  }
]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let's take one of these functions and execute it, using the API &lt;code&gt;/function/&lt;span class="highlight"&gt;function-name&lt;/span&gt;&lt;/code&gt; endpoint. This endpoint is available over the POST method, where the &lt;code&gt;-d&lt;/code&gt; flag allows you to send data to the function. &lt;/p&gt;

&lt;p&gt;For example, let's run the following &lt;code&gt;curl&lt;/code&gt; command to execute the &lt;code&gt;echoit&lt;/code&gt; function, which comes with OpenFaaS out of the box and outputs the string you've sent it as a request. You can use the string &lt;code&gt;"Sammy The Shark"&lt;/code&gt; to demonstrate:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;curl -u &lt;span class="highlight"&gt;admin&lt;/span&gt;:&lt;span class="highlight"&gt;your_openfaas_password&lt;/span&gt; -X &lt;span class="highlight"&gt;POST&lt;/span&gt; https://&lt;span class="highlight"&gt;example.com&lt;/span&gt;/function/&lt;span class="highlight"&gt;func_echoit&lt;/span&gt; -d "&lt;span class="highlight"&gt;Sammy The Shark&lt;/span&gt;"
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The output will show you &lt;code&gt;Sammy The Shark&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Sammy The Shark
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you see an error, follow the on-screen logs to resolve the problem before continuing with the tutorial. You can also check the &lt;code&gt;gateway&lt;/code&gt; service's logs.&lt;/p&gt;

&lt;p&gt;At this point, you've used the OpenFaaS API to manage and execute your functions. Let's now take a look at the OpenFaaS Web UI.&lt;/p&gt;

&lt;h2 id="step-6-—-using-the-openfaas-web-ui"&gt;Step 6 — Using the OpenFaaS Web UI&lt;/h2&gt;

&lt;p&gt;OpenFaaS comes with a Web UI that you can use to add new and execute installed functions. In this step, you will install a function for generating QR Codes from the FaaS Store and generate a sample code.&lt;/p&gt;

&lt;p&gt;To begin, point your web browser to &lt;code&gt;https://&lt;span class="highlight"&gt;example.com&lt;/span&gt;/ui/&lt;/code&gt;. Note that the trailing slash is required to avoid a "not found" error. &lt;/p&gt;

&lt;p&gt;In the HTTP authentication dialogue box, enter the username and password you got when deploying OpenFaaS in Step 3.&lt;/p&gt;

&lt;p&gt;Once logged in, you will see available functions on the left side of the screen, along with the &lt;strong&gt;Deploy New Functions&lt;/strong&gt; button used to install new functions.&lt;/p&gt;

&lt;p&gt;Click on &lt;strong&gt;Deploy New Functions&lt;/strong&gt; to deploy a new function. You will see the FaaS Store window, which provides community-tested functions that you can install with a single click:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/openfaas_ubuntu_1604/openfaas_func_store.png" alt="OpenFaaS Functions store"&gt;&lt;/p&gt;

&lt;p&gt;In addition to these functions, you can also deploy functions manually from a Docker image.&lt;/p&gt;

&lt;p&gt;For this tutorial, you will deploy the &lt;strong&gt;QR Code Generator&lt;/strong&gt; function from the FaaS Store. Locate the &lt;strong&gt;QR Code Generator - Go&lt;/strong&gt; item in the list, click on it, and then click the &lt;strong&gt;Deploy&lt;/strong&gt; button at the bottom of the window:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/openfaas_ubuntu_1604/openfaas_qr.png" alt="OpenFaaS QR Code Generator function"&gt;&lt;/p&gt;

&lt;p&gt;After clicking &lt;strong&gt;Deploy&lt;/strong&gt;, the &lt;strong&gt;Deploy A New Function&lt;/strong&gt; window will close and the function will be deployed. In the list at the left side of the window you will see a listing for the &lt;strong&gt;&lt;code&gt;qrcode-go&lt;/code&gt;&lt;/strong&gt; function. Click on this entry to select it. The main function window will show the function name, number of replicas, invocation count, and image, along with the option to invoke the function:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/openfaas_ubuntu_1604/openfaas_qr_code.png" alt="OpenFaaS QR Code Function"&gt;&lt;/p&gt;

&lt;p&gt;Let's generate a QR code containing the URL with your domain. In the &lt;strong&gt;Request body&lt;/strong&gt; field, type the content of the QR code you'd like to generate; in our case, this will be &lt;strong&gt;"example.com"&lt;/strong&gt;. Once you're done, click the &lt;strong&gt;Invoke&lt;/strong&gt; button. &lt;/p&gt;

&lt;p&gt;When you select either the &lt;strong&gt;Text&lt;/strong&gt; or &lt;strong&gt;JSON&lt;/strong&gt; output option, the function will output the file's content, which is not usable or human-readable:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/openfaas_ubuntu_1604/qr_generated_code.png" alt="OpenFaaS generated QR code"&gt;&lt;/p&gt;

&lt;p&gt;You can download a response. which in our case will be a PNG file with the QR code. To do this, select the &lt;strong&gt;Download&lt;/strong&gt; option, and then click &lt;strong&gt;Invoke&lt;/strong&gt; once again. Shortly after, you should have the QR code downloaded, which you can open with the image viewer of your choice:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/openfaas_ubuntu_1604/qr_image_two.png" alt="Generated QR code"&gt;&lt;/p&gt;

&lt;p&gt;In addition to deploying functions from the FaaS store or from Docker images, you can also create your own functions. In the next step, you will create a Python function using the FaaS command-line interface.&lt;/p&gt;

&lt;h2 id="step-7-—-creating-functions-with-the-faas-cli"&gt;Step 7 — Creating Functions With the FaaS CLI&lt;/h2&gt;

&lt;p&gt;In the previous steps, you configured the FaaS CLI to work with your OpenFaaS server. The FaaS CLI is a command-line interface that you can use to manage OpenFaaS and install and run functions, just like you would over the API or using the Web UI.&lt;/p&gt;

&lt;p&gt;Compared to the Web UI or the API, the FaaS CLI has templates for many programming languages that you can use to create your own functions. It can also build container images based on your function code and push images to an image registry, such as Docker Hub.&lt;/p&gt;

&lt;p&gt;In this step, you will create a function, publish it to Docker Hub, and then run it on your OpenFaaS server. This function will be similar to the default &lt;code&gt;echoit&lt;/code&gt; function, which returns input passed as a request.&lt;/p&gt;

&lt;p&gt;We will use Python to write our function. If you want to learn more about Python, you can check out our &lt;a href="https://www.digitalocean.com/community/tutorial_series/how-to-code-in-python-3"&gt;How To Code in Python 3 tutorial series&lt;/a&gt; and our &lt;a href="https://www.digitalocean.com/community/tutorials/digitalocean-ebook-how-to-code-in-python"&gt;How To Code in Python eBook&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Before creating the new function, let's create a directory to store FaaS functions and navigate to it:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;mkdir ~/faas-functions
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;cd ~/faas-functions
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Execute the following command to create a new Python function called &lt;code&gt;echo-input&lt;/code&gt;. Make sure to replace &lt;code&gt;&lt;span class="highlight"&gt;your-docker-hub-username&lt;/span&gt;&lt;/code&gt; with your Docker Hub username, as you'll push the function to Docker Hub later:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;faas-cli new &lt;span class="highlight"&gt;echo-input&lt;/span&gt; --lang &lt;span class="highlight"&gt;python&lt;/span&gt; --prefix &lt;span class="highlight"&gt;your-docker-hub-username&lt;/span&gt; --gateway &lt;span class="highlight"&gt;https://example.com&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The output contains confirmation about the successful function creation. If you don't have templates downloaded, the CLI will download templates in your current directory: &lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;2018/05/13 12:13:06 No templates found in current directory.
2018/05/13 12:13:06 Attempting to expand templates from https://github.com/openfaas/templates.git
2018/05/13 12:13:11 Fetched 12 template(s) : [csharp dockerfile go go-armhf node node-arm64 node-armhf python python-armhf python3 python3-armhf ruby] from https://github.com/openfaas/templates.git
Folder: &lt;span class="highlight"&gt;echo-input&lt;/span&gt; created.
  ___                   _____           ____
 / _ \ _ __   ___ _ __ |  ___|_ _  __ _/ ___|
| | | | '_ \ / _ \ '_ \| |_ / _` |/ _` \___ \
| |_| | |_) |  __/ | | |  _| (_| | (_| |___) |
 \___/| .__/ \___|_| |_|_|  \__,_|\__,_|____/
      |_|


Function created in folder: &lt;span class="highlight"&gt;echo-input&lt;/span&gt;
Stack file written: &lt;span class="highlight"&gt;echo-input&lt;/span&gt;.yml
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The result of the &lt;code&gt;faas-cli new&lt;/code&gt; command is a newly-created &lt;code&gt;~/faas-fucntions/echo-input&lt;/code&gt; directory containing the function's code and the &lt;code&gt;echo-input.yml&lt;/code&gt; file. This file includes information about your function: what language it's in, its name, and the server you will deploy it on. &lt;/p&gt;

&lt;p&gt;Navigate to the &lt;code&gt;~/faas-fucntions/echo-input&lt;/code&gt; directory:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cd ~/faas-fucntions/echo-input
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To see content of the directory, execute:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;ls
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The directory contains two files: &lt;code&gt;handler.py&lt;/code&gt;, which contains the code for your function, and &lt;code&gt;requirements.txt&lt;/code&gt;, which contains the Python modules required by the function.&lt;/p&gt;

&lt;p&gt;Since we don't currently require any non-default Python modules, the &lt;code&gt;requirements.txt&lt;/code&gt; file is empty. You can check that by using the &lt;code&gt;cat&lt;/code&gt; command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cat requirements.txt
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, let's write a function that will return a request as a string. &lt;/p&gt;

&lt;p&gt;The &lt;code&gt;handler.py&lt;/code&gt; file already has the sample handler code, which returns a received response as a string. Let's take a look at the code:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nano handler.py
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The default function is called &lt;code&gt;handle&lt;/code&gt; and takes a single parameter, &lt;code&gt;req&lt;/code&gt;, that contains a request that's passed to the function when it's invoked. The function does only one thing, returning the passed request back as the response:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-python"&gt;def handle(req):
    """handle a request to the function
    Args:
        req (str): request body
    """

    return req
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let's modify it to include additional text, replacing the string in the &lt;code&gt;return&lt;/code&gt; directive as follows:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-python"&gt;    return "Received message: " + req
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once you're done, save the file and close your text editor.&lt;/p&gt;

&lt;p&gt;Next, let's build a Docker image from the function's source code. Navigate to the &lt;code&gt;faas-functions&lt;/code&gt; directory where the &lt;code&gt;echo-input.yml&lt;/code&gt; file is located:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cd ~/faas-functions
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The following command builds the Docker image for your function:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;faas-cli build -f &lt;span class="highlight"&gt;echo-input.yml&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The output contains information about the build progress:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;[0] &amp;gt; Building echo-input.
Clearing temporary build folder: ./build/echo-input/
Preparing ./echo-input/ ./build/echo-input/function
Building: sammy/echo-input with python template. Please wait..
Sending build context to Docker daemon  7.168kB
Step 1/16 : FROM python:2.7-alpine
 ---&amp;gt; 5fdd069daf25
Step 2/16 : RUN apk --no-cache add curl     &amp;amp;&amp;amp; echo "Pulling watchdog binary from Github."     &amp;amp;&amp;amp; curl -sSL https://github.com/openfaas/faas/releases/download/0.8.0/fwatchdog &amp;gt; /usr/bin/fwatchdog     &amp;amp;&amp;amp; chmod +x /usr/bin/fwatchdog     &amp;amp;&amp;amp; apk del curl --no-cache
 ---&amp;gt; Using cache
 ---&amp;gt; 247d4772623a
Step 3/16 : WORKDIR /root/
 ---&amp;gt; Using cache
 ---&amp;gt; 532cc683d67b
Step 4/16 : COPY index.py           .
 ---&amp;gt; Using cache
 ---&amp;gt; b4b512152257
Step 5/16 : COPY requirements.txt   .
 ---&amp;gt; Using cache
 ---&amp;gt; 3f9cbb311ab4
Step 6/16 : RUN pip install -r requirements.txt
 ---&amp;gt; Using cache
 ---&amp;gt; dd7415c792b1
Step 7/16 : RUN mkdir -p function
 ---&amp;gt; Using cache
 ---&amp;gt; 96c25051cefc
Step 8/16 : RUN touch ./function/__init__.py
 ---&amp;gt; Using cache
 ---&amp;gt; 77a9db274e32
Step 9/16 : WORKDIR /root/function/
 ---&amp;gt; Using cache
 ---&amp;gt; 88a876eca9e3
Step 10/16 : COPY function/requirements.txt .
 ---&amp;gt; Using cache
 ---&amp;gt; f9ba5effdc5a
Step 11/16 : RUN pip install -r requirements.txt
 ---&amp;gt; Using cache
 ---&amp;gt; 394a1dd9e4d7
Step 12/16 : WORKDIR /root/
 ---&amp;gt; Using cache
 ---&amp;gt; 5a5893c25b65
Step 13/16 : COPY function           function
 ---&amp;gt; eeddfa67018d
Step 14/16 : ENV fprocess="python index.py"
 ---&amp;gt; Running in 8e53df4583f2
Removing intermediate container 8e53df4583f2
 ---&amp;gt; fb5086bc7f6c
Step 15/16 : HEALTHCHECK --interval=1s CMD [ -e /tmp/.lock ] || exit 1
 ---&amp;gt; Running in b38681a71378
Removing intermediate container b38681a71378
 ---&amp;gt; b04c045b0994
Step 16/16 : CMD ["fwatchdog"]
 ---&amp;gt; Running in c5a11078df3d
Removing intermediate container c5a11078df3d
 ---&amp;gt; bc5f08157c5a
Successfully built bc5f08157c5a
Successfully tagged sammy/echo-input:latest
Image: &lt;span class="highlight"&gt;your-docker-hub-username&lt;/span&gt;/echo-input built.
[0] &amp;lt; Building echo-input done.
[0] worker done.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you get an error, make sure to resolve it by following the on-screen instructions before deploying the function.&lt;/p&gt;

&lt;p&gt;You will need to containerize your OpenFaaS function in order to deploy it. Containerizing applications ensures that the environment needed to run your application can be easily reproduced, and your application can be easily deployed, scaled, and updated.&lt;/p&gt;

&lt;p&gt;For this tutorial, we'll use Docker Hub, as it's a free solution, but you can use any container registry, including your own private registry. &lt;/p&gt;

&lt;p&gt;Run the following command to push the image you built to your specified repository on Docker Hub:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;faas-cli push -f &lt;span class="highlight"&gt;echo-input.yml&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pushing will take several minutes, depending on your internet connection speed. The output contains the image's upload progress:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;[0] &amp;gt; Pushing echo-input.
The push refers to repository [docker.io/sammy/echo-input]
320ea573b385: Pushed 
9d87e56f5d0c: Pushed 
6f79b75e7434: Pushed 
23aac2d8ecf2: Pushed 
2bec17d09b7e: Pushed 
e5a0e5ab3be6: Pushed 
e9c8ca932f1b: Pushed 
beae1d55b4ce: Pushed 
2fcae03ed1f7: Pushed 
62103d5daa03: Mounted from library/python 
f6ac6def937b: Mounted from library/python 
55c108c7613c: Mounted from library/python 
e53f74215d12: Mounted from library/python 
latest: digest: sha256:794fa942c2f593286370bbab2b6c6b75b9c4dcde84f62f522e59fb0f52ba05c1 size: 3033
[0] &amp;lt; Pushing echo-input done.
[0] worker done.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, with your image pushed to Docker Hub, you can use it to deploy a function to your OpenFaaS server.&lt;/p&gt;

&lt;p&gt;To deploy your function, run the &lt;code&gt;deploy&lt;/code&gt; command, which takes the path to the manifest that describes your function, as well as the address of your OpenFaaS server:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;faas-cli deploy -f &lt;span class="highlight"&gt;echo-input.yml&lt;/span&gt; --gateway &lt;span class="highlight"&gt;https://example.com&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The output shows the status of the deployment, along with the name of the function you're deploying and the deployment status code:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Deploying: &lt;span class="highlight"&gt;echo-input&lt;/span&gt;.

Deployed. 200 OK.
URL: &lt;span class="highlight"&gt;https://example.com/function/echo-input&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If the deployment is successful, you will see a &lt;code&gt;200&lt;/code&gt; status code. In the case of errors, follow the provided instructions to fix the problem before continuing.&lt;/p&gt;

&lt;p&gt;At this point your function is deployed and ready to be used. You can test that it is working as expected by invoking it.&lt;/p&gt;

&lt;p&gt;To invoke a function with the FaaS CLI, use the &lt;code&gt;invoke&lt;/code&gt; command by passing the function name and OpenFaaS address to it. After executing the command, you'll be asked to enter the request you want to send to the function.&lt;/p&gt;

&lt;p&gt;Execute the following command to invoke the &lt;code&gt;echo-input&lt;/code&gt; function:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;faas-cli invoke echo-input --gateway &lt;span class="highlight"&gt;https://example.com&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You'll be asked to enter the request you want to send to the function:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Reading from STDIN - hit (Control + D) to stop.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Enter the text you want to send to the function, such as:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;Sammy The Shark!
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once you're done, press &lt;code&gt;ENTER&lt;/code&gt; and then &lt;code&gt;CTRL + D&lt;/code&gt; to finish the request. The &lt;code&gt;CTRL + D&lt;/code&gt; shortcut in the terminal is used to register an End-of-File (EOF). The OpenFaaS CLI stops reading from the terminal once EOF is received.&lt;/p&gt;

&lt;p&gt;After several seconds, the command will output the function's response:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Reading from STDIN - hit (Control + D) to stop.
Sammy The Shark!
Received message: Sammy The Shark!
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you don't see the output or you get an error, retrace the preceding steps to make sure you've deployed the function as explained and follow the on-screen instructions to resolve the problem.&lt;/p&gt;

&lt;p&gt;At this point, you've interacted with your function using three methods: the Web UI, the API, and the CLI. Being able to execute your functions with any of these methods offers you the flexibility of deciding how you would like to integrate functions into your existing workflows.&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;In this tutorial, you've used serverless architecture and OpenFaaS to deploy and manage your applications using the OpenFaaS API, Web UI, and CLI. You also secured your infrastructure by leveraging Traefik to provide SSL using Let's Encrypt.&lt;/p&gt;

&lt;p&gt;If you want to learn more about the OpenFaaS project, you can check out their &lt;a href="https://www.openfaas.com/"&gt;website&lt;/a&gt; and the &lt;a href="https://docs.openfaas.com/"&gt;project's official documentation&lt;/a&gt;. &lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/white-paper-running-cloud-native-applications-on-digitalocean-kubernetes</id>
    <published>2018-09-17T19:51:30Z</published>
    <updated>2018-09-19T19:36:34Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/white-paper-running-cloud-native-applications-on-digitalocean-kubernetes"/>
    <title>White Paper: Running Cloud Native Applications on DigitalOcean Kubernetes</title>
    <content type="html">&lt;p&gt;&lt;div class='code-label notes-and-warnings note' title='&lt;strong&gt;Download the Kubernetes White Paper&lt;/strong&gt;'&gt;&lt;strong&gt;Download the Kubernetes White Paper&lt;/strong&gt;&lt;/div&gt;&lt;span class='note'&gt;
&lt;a href="https://do.co/k8s-wp"&gt;&lt;strong&gt;Running Cloud Native Applications on DigitalOcean Kubernetes&lt;/strong&gt; (PDF)&lt;/a&gt;&lt;br&gt;&lt;/span&gt;&lt;/p&gt;

&lt;h2 id="abstract"&gt;Abstract&lt;/h2&gt;

&lt;p&gt;The &lt;em&gt;Running Cloud Native Applications on DigitalOcean Kubernetes&lt;/em&gt; White Paper brings readers through a variety of cloud native topics, introducing them to how they may leverage Kubernetes in order to manage and scale their applications. &lt;/p&gt;

&lt;p&gt;This white paper provides further insight into:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Trends in Modern Application Development&lt;/li&gt;
&lt;li&gt;The Cloud Native Ecosystem&lt;/li&gt;
&lt;li&gt;Microservices&lt;/li&gt;
&lt;li&gt;Containers&lt;/li&gt;
&lt;li&gt;Clusters&lt;/li&gt;
&lt;li&gt;Kubernetes and DigitalOcean Kubernetes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Throughout the White Paper, a photo-sharing app called “Snappy” is used as a running example to demonstrate the value of implementing Cloud Native best practices. &lt;/p&gt;

&lt;h2 id="executive-summary-scaling-cloud-native-apps"&gt;Executive Summary: Scaling Cloud Native Apps&lt;/h2&gt;

&lt;p&gt;In today’s fast-moving software landscape, advances in operations technologies have fostered the dramatic reduction of application release cycles. Traditionally, software releases follow a time-based schedule, but it has become increasingly common to see applications and services continuously delivered and deployed to users throughout the day. This truncating of the traditional software release cycle has its roots both in technological developments — such as the explosive growth of cloud platforms, containers, and microservices-oriented architectures — as well as cultural developments — with tech-savvy and mobile-enabled users increasingly expecting new features, fast bug fixes, and a responsive and continuously developing product.&lt;/p&gt;

&lt;p&gt;This symbiotic relationship between end users and developers has become increasingly linked. Shifting organizational structures and application architectures allow developers to quickly incorporate feedback and react to user demands. This accelerated development cadence often accompanies the packaging of applications into containers, and the use of systems that automate their deployment and orchestration, like Docker Swarm, Marathon, and Kubernetes. These open-source platforms, now stable enough for large-scale production deployments, allow service owners to launch and scale applications themselves, effortlessly managing hundreds of running containers.&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/k8s-wp/k8s-diagram.png" alt="Kuberntes Diagram"&gt;&lt;/p&gt;

&lt;h2 id="kubernetes-and-digitalocean-kubernetes"&gt;Kubernetes and DigitalOcean Kubernetes&lt;/h2&gt;

&lt;p&gt;Kubernetes, initially open-sourced by Google in 2014, has today grown to become one of the highest velocity projects on GitHub, with over 11,300 contributing developers and 75,000 commits. The growth of its thriving open-source community mirrors its popularity in the private sector, with over 50% of Fortune 100 companies relying on Kubernetes every day to rapidly deploy new features and bug fixes to users.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.digitalocean.com/products/kubernetes/"&gt;DigitalOcean Kubernetes&lt;/a&gt; enables development teams both small and large to quickly take advantage of this market-leading container orchestration platform without the lead time required to provision, install, and operate a cluster. With its simplicity and developer-friendly interfaces, DigitalOcean Kubernetes empowers developers to launch their containerized applications into a managed, production-ready cluster without having to maintain and configure the underlying infrastructure. Seamlessly integrating with the rest of the DigitalOcean suite — including Load Balancers, Firewalls, Object Storage Spaces, and Block Storage Volumes — and with built-in support for public and private image registries like Docker Hub and Quay.io, developers can now run and scale container-based workloads with ease on the DigitalOcean platform.&lt;/p&gt;

&lt;p&gt;With full programmatic control of their cluster using the exposed Kubernetes REST API, developers can benefit from the rich ecosystem of open-source tools while still reaping the convenience of managed infrastructure. Teams can flexibly deploy and scale their Cloud Native applications. A Certified Kubernetes conformant platform, DigitalOcean Kubernetes helps developers launch their application containers and bring their Kubernetes workloads into the DigitalOcean cloud with minimal configuration and operations overhead.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;To learn more about scaling and managing Cloud Native applications, microservices, containers, and Kubernetes, download your free copy of&lt;/em&gt; &lt;strong&gt;Running Cloud Native Applications on DigitalOcean Kubernetes&lt;/strong&gt;&lt;em&gt;!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;div class='code-label notes-and-warnings note' title='&lt;strong&gt;Download the Kubernetes White Paper&lt;/strong&gt;'&gt;&lt;strong&gt;Download the Kubernetes White Paper&lt;/strong&gt;&lt;/div&gt;&lt;span class='note'&gt;
&lt;a href="https://do.co/k8s-wp"&gt;&lt;strong&gt;Running Cloud Native Applications on DigitalOcean Kubernetes&lt;/strong&gt; (PDF)&lt;/a&gt;&lt;br&gt;&lt;/span&gt;&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/como-construir-imagens-docker-e-hospedar-um-repositorio-de-imagens-docker-com-o-gitlab-pt</id>
    <published>2018-09-17T20:19:11Z</published>
    <updated>2018-10-17T00:35:43Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/como-construir-imagens-docker-e-hospedar-um-repositorio-de-imagens-docker-com-o-gitlab-pt"/>
    <title>Como Construir Imagens Docker e Hospedar um Repositório de Imagens Docker com o GitLab</title>
    <content type="html">&lt;h1 id="como-construir-imagens-docker-e-hospedar-um-repositório-de-imagens-docker-com-o-gitlab"&gt;Como Construir Imagens Docker e Hospedar um Repositório de Imagens Docker com o GitLab&lt;/h1&gt;

&lt;h2 id="introdução"&gt;Introdução&lt;/h2&gt;

&lt;p&gt;A containerização está rapidamente se tornando o método de empacotamento e deploy de aplicações mais aceito nos ambientes de nuvem. A padronização que ele fornece, juntamente com sua eficiência de recursos (quando comparado a máquinas virtuais completas) e flexibilidade, o tornam um grande facilitador da moderna mentalidade &lt;em&gt;DevOps&lt;/em&gt;. Muitas estratégias interessantes de deployment, orquestração e monitoramento &lt;em&gt;nativas para nuvem&lt;/em&gt; tornam-se possíveis quando suas aplicações e microsserviços são totalmente containerizados. &lt;/p&gt;

&lt;p&gt;Os containers &lt;a href="https://www.docker.com/"&gt;Docker&lt;/a&gt; são de longe os tipos mais comuns de container atualmente. Embora os repositórios públicos de imagem do Docker como o &lt;a href="https://hub.docker.com/"&gt;Docker Hub&lt;/a&gt; estejam repletos de imagens de software opensource containerizado que você pode fazer um &lt;code&gt;docker pull&lt;/code&gt; hoje, para código privado você precisará pagar um serviço para construir e armazenar suas imagens, ou executar seu próprio software para fazer isso.  &lt;/p&gt;

&lt;p&gt;O &lt;a href="https://about.gitlab.com/"&gt;GitLab&lt;/a&gt; Community Edition é um pacote de software auto-hospedado que fornece hospedagem de repositório Git, acompanhamento de projetos, serviços de CI/CD, e um registro de imagem Docker, entre outros recursos. Neste tutorial vamos utilizar o serviço de integração contínua do GitLab para construir imagens Docker a partir de uma aplicação de exemplo em Node.js. Estas imagens serão então testadas e carregadas para o nosso próprio registro privado do Docker.  &lt;/p&gt;

&lt;h2 id="pré-requisitos"&gt;Pré-requisitos&lt;/h2&gt;

&lt;p&gt;Antes de começarmos, precisamos configurar &lt;strong&gt;um servidor GitLab seguro&lt;/strong&gt;, e &lt;strong&gt;um GitLab CI runner&lt;/strong&gt; para executar tarefas de integração contínua. As seções abaixo fornecerão links e maiores detalhes.&lt;/p&gt;

&lt;h3 id="um-servidor-gitlab-protegido-com-ssl"&gt;Um Servidor Gitlab Protegido com SSL&lt;/h3&gt;

&lt;p&gt;Para armazenar nosso código fonte, executar tarefas de CI/CD, e hospedar um registro Docker, precisamos de uma instância do GitLab instalada em um servidor Ubuntu 16.04. Atualmente, o GitLab recomenda &lt;strong&gt;um servidor com pelo menos 2 núcleos de CPU e 4GB de RAM&lt;/strong&gt;. Adicionalmente, iremos proteger o servidor com certificados SSL do Let's Encrypt. Para fazer isto, precisaremos de um nome de domínio apontando para o servidor.&lt;/p&gt;

&lt;p&gt;Você pode completar esses pré-requisitos com os seguintes tutoriais:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.digitalocean.com/community/tutorials/how-to-set-up-a-host-name-with-digitalocean"&gt;Como configurar um nome de host com a DigitalOcean&lt;/a&gt; mostrará como gerenciar um domínio com o painel de controle da DigitalOcean.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.digitalocean.com/community/tutorials/configuracao-inicial-de-servidor-com-ubuntu-16-04-pt"&gt;Configuração Inicial de servidor com Ubuntu 16.04&lt;/a&gt; vai lhe fornecer um usuário não-root, habilitado para sudo, e habilitar o firewall &lt;code&gt;ufw&lt;/code&gt; do Ubuntu.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.digitalocean.com/community/tutorials/como-instalar-e-configurar-o-gitlab-no-ubuntu-16-04-pt"&gt;Como instalar e configurar o GitLab no Ubuntu 16.04&lt;/a&gt; irá lhe mostrar como instalar o GitLab e configurá-lo com um certificado TLS/SSL gratuito do Let's Encrypt&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id="um-gitlab-ci-runner"&gt;Um GitLab CI Runner&lt;/h3&gt;

&lt;p&gt;O tutorial &lt;a href="https://www.digitalocean.com/community/tutorials/como-configurar-pipelines-de-integracao-continua-com-o-gitlab-ci-no-ubuntu-16-04-pt"&gt;Como configurar pipelines de integração contínua com o GitLab CI no Ubuntu 16.04&lt;/a&gt; fornecerá uma visão geral do serviço de CI ou integração contínua do GitLab e mostrará como configurar um CI runner para processar jobs. Vamos construir isso em cima da aplicação de demonstração e da infraestrutura do runner criados neste tutorial.&lt;/p&gt;

&lt;h2 id="passo-1-—-configurando-um-gitlab-ci-runner-privilegiado"&gt;Passo 1 — Configurando um GitLab CI Runner Privilegiado&lt;/h2&gt;

&lt;p&gt;No pré-requisito do tutorial de integração contínua com o GitLab, configuramos um GitLab runner utilizando &lt;code&gt;sudo gitlab-runner register&lt;/code&gt; e seu processo de configuração interativo. Este runner é capaz de executar builds e testes de software dentro de containers Docker isolados.&lt;/p&gt;

&lt;p&gt;Entretanto, para se construir imagens Docker, nosso runner precisa de acesso total ao próprio serviço do Docker. A maneira recomendada de se configurar isto é utilizar a imagem &lt;code&gt;docker-in-docker&lt;/code&gt; oficial do Docker para executar os jobs. Isto requer conceder ao runner um modo de execução &lt;code&gt;privileged&lt;/code&gt; ou privilegiado. Portanto, criaremos um segundo runner com este modo ativado.&lt;/p&gt;

&lt;p&gt;&lt;span class='note'&gt;&lt;strong&gt;Nota:&lt;/strong&gt; Conceder ao runner o modo &lt;strong&gt;privileged&lt;/strong&gt; basicamente desativa todas as vantagens de segurança da utilização de containers. Infelizmente, os outros métodos de ativar runners compatíveis com o Docker também carregam implicações de segurança semelhantes. Por favor, veja &lt;a href="https://docs.gitlab.com/ce/ci/docker/using_docker_build.html"&gt;a documentação oficial do GitLab no Docker Build&lt;/a&gt; para aprender mais sobre as diferentes opções de runners e qual é a melhor para a sua situação.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;Como existem implicações de segurança para a utilização de runner privilegiado, vamos criar um runner específico do projeto que aceitará somente jobs de Docker em nosso projeto &lt;code&gt;hello_hapi&lt;/code&gt; (Os administradores de GitLab sempre podem adicionar manualmente esse runner a outros projetos posteriormente). A partir da página do nosso projeto &lt;code&gt;hello_hapi&lt;/code&gt;, clique em &lt;strong&gt;Settings&lt;/strong&gt; na parte inferior do menu à esquerda, em seguida clique em &lt;strong&gt;CI/CD&lt;/strong&gt; no sub-menu: &lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/gitlab-docker/settings-ci.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Agora, clique no botão &lt;strong&gt;Expand&lt;/strong&gt; ao lado da seção de configurações de &lt;strong&gt;Runners&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/gitlab-docker/runner-expand.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Haverá algumas informações sobre como configurar um &lt;strong&gt;Specific Runner&lt;/strong&gt;, incluindo um token de registro. Tome nota desse token. Quando o utilizamos para registrar um novo runner, o runner será bloqueado apenas para este projeto.&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/gitlab-docker/runner-token.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Estando nesta página, clique no botão &lt;strong&gt;Disable shared Runners&lt;/strong&gt;. Queremos ter certeza de que nossos jobs de Docker sempre executarão em nosso runner privilegiado. Se um runner compartilhado não privilegiado estivesse disponível, o GitLab pode optar por utilizá-lo, o que resultaria em erros de build.&lt;/p&gt;

&lt;p&gt;Faça o login no servidor que possui o seu CI runner atual. Se você não tiver uma máquina já configurada com os runners, volte e complete a seção &lt;a href="https://www.digitalocean.com/community/tutorials/como-configurar-pipelines-de-integracao-continua-com-o-gitlab-ci-no-ubuntu-16-04-pt"&gt;Instalando o Serviço CI Runner do GitLab&lt;/a&gt; do tutorial de pré-requisitos antes de continuar. &lt;/p&gt;

&lt;p&gt;Agora, execute o seguinte comando para configurar o runner privilegiado específico do projeto:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo gitlab-runner register -n \
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;  --url https://&lt;span class="highlight"&gt;gitlab.example.com&lt;/span&gt;/ \
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;  --registration-token &lt;span class="highlight"&gt;seu-token&lt;/span&gt; \
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;  --executor docker \
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;  --description "&lt;span class="highlight"&gt;docker-builder&lt;/span&gt;" \
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;  --docker-image "docker:latest" \
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;  --docker-privileged
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Registering runner... succeeded                     runner=61SR6BwV
Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded!
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Certifique-se de substituir suas próprias informações. Nós definimos todas as opções do nosso runner na linha de comando em vez de usar os prompts interativos, porque os prompts não nos permitem especificar o modo &lt;code&gt;--docker-privileged&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Agora o seu runner está configurado, registrado e executando. Para verificar, volte ao seu navegador. Clique no ícone de chave inglesa na barra de menu principal do GitLab, em seguida clique em &lt;strong&gt;Runners&lt;/strong&gt; no menu à esquerda. Seus runners serão listados: &lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/gitlab-docker/runner-list.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Agora que temos um runner capaz de criar imagens do Docker, vamos configurar um registro privado do Docker para carregar imagens para ele.&lt;/p&gt;

&lt;h2 id="passo-2-—-configurando-o-registro-docker-do-gitlab"&gt;Passo 2 — Configurando o Registro Docker do GitLab&lt;/h2&gt;

&lt;p&gt;Configurar seu próprio registro do Docker permite que você envie e extraia imagens de seu próprio servidor privado, aumentando a segurança e reduzindo as dependências do seu fluxo de trabalho em serviços externos.&lt;/p&gt;

&lt;p&gt;O GitLab irá configurar um registro Docker privado com apenas algumas atualizações de configuração. Primeiro vamos configurar a URL onde o registro irá residir. Depois, iremos (opcionalmente) configurar o registro para usar um serviço de armazenamento de objetos compatível com S3 para armazenar seus dados.&lt;/p&gt;

&lt;p&gt;Faça SSH em seu servidor GitLab, depois abra o arquivo de configuração do GitLab:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/gitlab/gitlab.rb
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Role para baixo até a seção &lt;strong&gt;Container Registry settings&lt;/strong&gt;. Vamos descomentar a linha &lt;code&gt;registry_external_url&lt;/code&gt; e configurá-la para o nosso host GitLab com a porta número &lt;code&gt;5555&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-label " title="/etc/gitlab/gitlab.rb"&gt;/etc/gitlab/gitlab.rb&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;
registry_external_url 'https://&lt;span class="highlight"&gt;gitlab.example.com&lt;/span&gt;:5555'
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A seguir, adicione as duas linhas seguintes para dizer ao registro onde encontrar nossos certificados Let's Encrypt: &lt;/p&gt;
&lt;div class="code-label " title="/etc/gitlab/gitlab.rb"&gt;/etc/gitlab/gitlab.rb&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;
registry_nginx['ssl_certificate'] = "/etc/letsencrypt/live/&lt;span class="highlight"&gt;gitlab.example.com&lt;/span&gt;/fullchain.pem"
registry_nginx['ssl_certificate_key'] = "/etc/letsencrypt/live/&lt;span class="highlight"&gt;gitlab.example.com&lt;/span&gt;/privkey.pem"
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Salve e feche o arquivo, depois reconfigure o GitLab:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo gitlab-ctl reconfigure
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;. . .
gitlab Reconfigured!
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Atualize o firewall para pemitir tráfego para a porta do registro:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw allow 5555
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Agora mude para outra máquina com o Docker instalado e efetue o login no registro Docker privado. Se você não tiver o Docker no seu computador de desenvolvimento local, você pode usar qualquer servidor configurado para executar seus jobs do GitLab CI, já que ele tem o Docker instalado:&lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;docker login &lt;span class="highlight"&gt;gitlab.example.com&lt;/span&gt;:5555
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Você será solicitado para inserir o seu nome de usuário e senha. Use suas credenciais do GitLab para efetuar login.&lt;/p&gt;
&lt;pre class="code-pre  local-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Login Succeeded
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Sucesso! O registro está configurado e funcionando. Atualmente, ele armazenará arquivos no sistema de arquivos local do servidor GitLab. Se você quiser usar um serviço de armazenamento de objetos, continue com esta seção. Se não, pule para o Passo 3. &lt;/p&gt;

&lt;p&gt;Para configurar um backend de armazenamento de objetos para o registro, precisamos saber as seguintes informações sobre o nosso serviço de armazenamento de objetos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Access Key&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Secret Key&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Region&lt;/strong&gt; (&lt;code&gt;us-east-1&lt;/code&gt;) por exemplo, se estiver usando Amazon S3, ou &lt;strong&gt;Region Endpoint&lt;/strong&gt; se estiver usando um serviço compatível com S3 (&lt;code&gt;https://nyc.digitaloceanspaces.com&lt;/code&gt;)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Nome do Bucket&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Se você estiver usando o DigitalOcean Spaces, você pode descobrir como configurar um novo Space e obter as informações acima lendo &lt;a href="https://www.digitalocean.com/community/tutorials/como-criar-um-space-e-uma-api-key-na-digitalocean-pt"&gt;Como Criar um Space e uma Chave de API na DigitalOcean&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Quando você tiver suas informações sobre o amazenamento de objetos, abra o arquivo de configuração do GitLab: &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/gitlab/gitlab.rb
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Novamente, role até a seção de registro do container. Procure pelo bloco &lt;code&gt;registry['storage']&lt;/code&gt;, descomente o bloco e atualize-o para o seguinte, novamente certificando-se de substituir suas próprias informações, quando apropriado:  &lt;/p&gt;
&lt;div class="code-label " title="/etc/gitlab/gitlab.rb"&gt;/etc/gitlab/gitlab.rb&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;
registry['storage'] = {
  's3' =&amp;gt; {
    'accesskey' =&amp;gt; '&lt;span class="highlight"&gt;sua-key&lt;/span&gt;',
    'secretkey' =&amp;gt; '&lt;span class="highlight"&gt;seu-secret&lt;/span&gt;',
    'bucket' =&amp;gt; '&lt;span class="highlight"&gt;seu-bucket-name&lt;/span&gt;',
    'region' =&amp;gt; '&lt;span class="highlight"&gt;nyc3&lt;/span&gt;',
    'regionendpoint' =&amp;gt; '&lt;span class="highlight"&gt;https://nyc3.digitaloceanspaces.com&lt;/span&gt;'
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Se você estiver uando Amazon S3, você precisa apenas da &lt;code&gt;region&lt;/code&gt; e não do &lt;code&gt;regionendpoint&lt;/code&gt;. Se estiver usando um serviço S3 compatível, como o Spaces, você irá precisar do &lt;code&gt;regionendpoint&lt;/code&gt;. Neste caso, &lt;code&gt;region&lt;/code&gt; na verdade não configura nada e o valor que você digita não importa, mas ainda precisa estar presente e não em branco.&lt;/p&gt;

&lt;p&gt;Salve e feche o arquivo.&lt;/p&gt;

&lt;span class='note'&gt;&lt;p&gt;
&lt;strong&gt;Nota:&lt;/strong&gt; Atualmente, há um bug em que o registro será encerrado após trinta segundos se seu bucket de armazenamento de objetos estiver vazio. Para evitar isso, coloque um arquivo no seu bucket antes de executar a próxima etapa. Você poderá removê-lo mais tarde, após o registro ter adicionado seus próprios objetos.&lt;/p&gt;

&lt;p&gt;Se você estiver usando o Spaces da DigitalOcean, você pode arrastar e soltar um arquivo para carregá-lo usando a interface do Painel de Controle.&lt;br&gt;&lt;/p&gt;&lt;/span&gt;

&lt;p&gt;Reconfigure o GitLab mais uma vez:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo gitlab-ctl reconfigure
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Em sua outra máquina Docker, efetue login no registro novamente para ter certeza de que tudo está bem:&lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;docker login &lt;span class="highlight"&gt;gitlab.example.com&lt;/span&gt;:5555
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Você deve receber uma mensagem de &lt;code&gt;Login Succeeded&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Agora que temos nosso registro do Docker configurado, vamos atualizar a configuração de CI da nossa aplicação para criar e testar nossa app, e enviar as imagens Docker para o nosso registro privado. &lt;/p&gt;

&lt;h2 id="passo-3-—-atualizando-o-gitlab-ci-yaml-e-construindo-uma-imagem-docker"&gt;Passo 3 — Atualizando o &lt;code&gt;gitlab-ci.yaml&lt;/code&gt; e Construindo uma Imagem Docker&lt;/h2&gt;

&lt;p&gt;&lt;span class='note'&gt;&lt;strong&gt;Nota:&lt;/strong&gt; Se você não concluiu o &lt;a href="https://www.digitalocean.com/community/tutorials/como-configurar-pipelines-de-integracao-continua-com-o-gitlab-ci-no-ubuntu-16-04-pt"&gt;artigo de pré-requisito do GitLab CI&lt;/a&gt; você precisará copiar o repositório de exemplo para o seu servidor GitLab. Siga a seção &lt;a href="https://www.digitalocean.com/community/tutorials/como-configurar-pipelines-de-integracao-continua-com-o-gitlab-ci-no-ubuntu-16-04-pt"&gt;Copiando o Repositório de Exemplo a partir do GitHub&lt;/a&gt; para fazer isto. &lt;br&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;Para que possamos fazer o building de nossa app no Docker, precisamos atualizar o arquivo &lt;code&gt;.gitlab-ci.yml&lt;/code&gt;. Você pode editar este arquivo diretamente no GitLab clicando na página principal do projeto, e depois no botão &lt;strong&gt;Edit&lt;/strong&gt;. Alternativamente, você poderia clonar o repositório para a sua máquina local, editar o arquivo, e então fazer um &lt;code&gt;git push&lt;/code&gt; nele de volta para o GitLab. Isso ficaria assim:  &lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;git clone git@gitlab.example.com:sammy/hello_hapi.git
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;cd hello_hapi
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;# edit the file w/ your favorite editor
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;git commit -am "updating ci configuration"
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;git push
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Primeiro, exclua tudo no arquivo, depois cole nele a seguinte configuração:&lt;/p&gt;
&lt;div class="code-label " title=".gitlab-ci.yml"&gt;.gitlab-ci.yml&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;
image: docker:latest
services:
- docker:dind

stages:
- build
- test
- release

variables:
  TEST_IMAGE: &lt;span class="highlight"&gt;gitlab.example.com:5555/sammy&lt;/span&gt;/hello_hapi:$CI_COMMIT_REF_NAME
  RELEASE_IMAGE: &lt;span class="highlight"&gt;gitlab.example.com:5555/sammy&lt;/span&gt;/hello_hapi:latest

before_script:
  - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN &lt;span class="highlight"&gt;gitlab.example.com:5555&lt;/span&gt;

build:
  stage: build
  script:
    - docker build --pull -t $TEST_IMAGE .
    - docker push $TEST_IMAGE

test:
  stage: test
  script:
    - docker pull $TEST_IMAGE
    - docker run $TEST_IMAGE npm test

release:
  stage: release
  script:
    - docker pull $TEST_IMAGE
    - docker tag $TEST_IMAGE $RELEASE_IMAGE
    - docker push $RELEASE_IMAGE
  only:
    - master
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Certifique-se de atualizar os URLs e nomes de usuários realçados com suas próprias informações e, em seguida, salve com o botão &lt;strong&gt;Commit changes&lt;/strong&gt; no GitLab. Se você está atualizando o arquivo fora do GitLab, confirme as mudanças e faça &lt;code&gt;git push&lt;/code&gt; de volta no GitLab.&lt;/p&gt;

&lt;p&gt;Este novo arquivo de configuração diz ao GitLab para usar a imagem mais recente do docker (&lt;code&gt;image: docker:latest&lt;/code&gt;) e vinculá-la ao serviço docker-in-docker (docker:dind). Então, ele define os estágios de &lt;code&gt;build&lt;/code&gt;, &lt;code&gt;test&lt;/code&gt;, e &lt;code&gt;release&lt;/code&gt;. O estágio de &lt;code&gt;build&lt;/code&gt; cria a imagem do Docker usando o &lt;code&gt;Dockerfile&lt;/code&gt; fornecido pelo repositório, em seguida o carrega para o nosso registro de imagens Docker. Se isso for bem sucedido, o estágio &lt;code&gt;test&lt;/code&gt; vai baixar a imagem que acabamos de construir e executar o comando &lt;code&gt;npm test&lt;/code&gt; dentro dele. Se o estágio &lt;code&gt;test&lt;/code&gt; for bem sucedido, o estágio &lt;code&gt;release&lt;/code&gt; irá lançar a imagem, irá colocar uma tag como &lt;code&gt;hello_hapi:latest&lt;/code&gt; e irá retorná-la ao registro. &lt;/p&gt;

&lt;p&gt;Dependendo do seu fluxo de trabalho, você também pode adicionar mais estágios &lt;code&gt;test&lt;/code&gt;, ou mesmo estágios &lt;code&gt;deploy&lt;/code&gt; que levam o aplicativo para um ambiente de preparação ou produção.&lt;/p&gt;

&lt;p&gt;A atualização do arquivo de configuração deve ter acionado um novo build. Volte ao projeto &lt;code&gt;hello_hapi&lt;/code&gt; no GitLab e clique no indicador de status do CI para o commit:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/gitlab-docker/commit-widget.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Na página resultante, você pode clicar em qualquer um dos estágios para ver seu progresso:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/gitlab-docker/commit-pipeline.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/gitlab-docker/stage-detail.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Eventualmente, todas as etapas devem indicar que eles foram bem sucedidos, mostrando ícones com a marca de verificação em verde. Podemos encontrar as imagens Docker que acabaram de ser construídas clicando no item &lt;strong&gt;Registry&lt;/strong&gt; no menu à esquerda:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/gitlab-docker/docker-list.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Se você clicar no pequeno ícone "document" ao lado do nome da imagem, ele copiará o comando apropriado &lt;code&gt;docker pull ...&lt;/code&gt; para a sua área de transferência. Você pode então baixar e executar sua imagem: &lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;docker pull &lt;span class="highlight"&gt;gitlab.example.com:5555/sammy/hello_hapi:latest&lt;/span&gt;
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;docker run -it --rm -p 3000:3000 &lt;span class="highlight"&gt;gitlab.example.com:5555/sammy/hello_hapi:latest&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre  local-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;&amp;gt; hello@1.0.0 start /usr/src/app
&amp;gt; node app.js

Server running at: http://56fd5df5ddd3:3000
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A imagem foi baixada do registro e iniciada em um container. Mude para o seu navegador e conecte-se ao aplicativo na porta 3000 para testar. Neste caso, estamos executando o container em nossa máquina local, assim podemos acessá-la via &lt;strong&gt;localhost&lt;/strong&gt; na seguinte URL:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;http://localhost:3000/hello/test
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Hello, test!
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Sucesso! Você pode parar o container com &lt;code&gt;CTRL-C&lt;/code&gt;. A partir de agora, toda vez que enviarmos um novo código para a ramificação master do nosso repositório, vamos construir e testar automaticamente uma nova imagem &lt;code&gt;hello_hapi: latest&lt;/code&gt;.  &lt;/p&gt;

&lt;h2 id="conclusão"&gt;Conclusão&lt;/h2&gt;

&lt;p&gt;Neste tutorial, configuramos um novo GitLab runner para criar imagens do Docker, criamos um regisro privado do Docker para armazená-las, e atualizamos um app Node.js para ser construído e testado dentro de containers Docker.&lt;/p&gt;

&lt;p&gt;Para aprender mais sobre os vários componentes utilizados nesta configuração, você pode ler a documentação oficial do &lt;a href="https://docs.gitlab.com/ce/README.html"&gt;GitLab CE&lt;/a&gt;, &lt;a href="https://docs.gitlab.com/ee/administration/container_registry.html"&gt;GitLab Container Registry&lt;/a&gt;, e do &lt;a href="https://docs.docker.com/"&gt;Docker&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;Por Brian Boucheron&lt;/em&gt;&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-install-wordpress-with-lemp-on-debian-9</id>
    <published>2018-09-14T16:28:22Z</published>
    <updated>2018-09-14T16:28:40Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-install-wordpress-with-lemp-on-debian-9"/>
    <title>How To Install WordPress with LEMP on Debian 9</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;WordPress is the most popular CMS (content management system) on the internet. It allows you to easily set up flexible blogs and websites on top of a MySQL backend with PHP processing. WordPress has seen incredible adoption and is a great choice for getting a website up and running quickly. After setup, almost all administration can be done through the web frontend.&lt;/p&gt;

&lt;p&gt;In this guide, we'll focus on getting a WordPress instance set up on a LEMP stack (Linux, Nginx, MySQL, and PHP) on a Debian 9 server.&lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;In order to complete this tutorial, you will need access to a Debian 9 server.&lt;/p&gt;

&lt;p&gt;You will need to perform the following tasks before you can start this guide:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Create a &lt;code&gt;sudo&lt;/code&gt; user on your server&lt;/strong&gt;: We will be completing the steps in this guide using a non-root user with &lt;code&gt;sudo&lt;/code&gt; privileges. You can create a user with &lt;code&gt;sudo&lt;/code&gt; privileges by following our &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-debian-9"&gt;Debian 9 initial server setup guide&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Install a LEMP stack&lt;/strong&gt;: WordPress will need a web server, a database, and PHP in order to correctly function. Setting up a LEMP stack (Linux, Nginx, MySQL, and PHP) fulfills all of these requirements. Follow &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-linux-nginx-mysql-php-lemp-stack-on-debian-9"&gt;this guide&lt;/a&gt; to install and configure this software.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Secure your site with SSL&lt;/strong&gt;: WordPress serves dynamic content and handles user authentication and authorization. TLS/SSL is the technology that allows you to encrypt the traffic from your site so that your connection is secure. This tutorial will assume that you have a domain name for your blog. You can use Let's Encrypt to get a free SSL certificate for your domain. Follow our &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-secure-nginx-with-let-s-encrypt-on-debian-9"&gt;Let's Encrypt guide for Nginx&lt;/a&gt; to set this up.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When you are finished the setup steps, log into your server as your &lt;code&gt;sudo&lt;/code&gt; user and continue below.&lt;/p&gt;

&lt;h2 id="step-1-—-creating-a-mysql-database-and-user-for-wordpress"&gt;Step 1 — Creating a MySQL Database and User for WordPress&lt;/h2&gt;

&lt;p&gt;The first step that we will take is a preparatory one. WordPress uses MySQL to manage and store site and user information. We have MySQL installed already, but we need to make a database and a user for WordPress to use.&lt;/p&gt;

&lt;p&gt;To get started, log into the MySQL root (administrative) account. If MySQL is configured to use the &lt;code&gt;auth_socket&lt;/code&gt; authentication plugin (the default), you can log into the MySQL administrative account using &lt;code&gt;sudo&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo mysql
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you changed the authentication method to use a password for the MySQL root account, use the following format instead:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;mysql -u root -p
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will be prompted for the password you set for the MySQL root account.&lt;/p&gt;

&lt;p&gt;First, we can create a separate database that WordPress can control. You can call this whatever you would like, but we will be using &lt;code&gt;wordpress&lt;/code&gt; in this guide to keep it simple. You can create the database for WordPress by typing:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;CREATE DATABASE &lt;span class="highlight"&gt;your_domain&lt;/span&gt; DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span class='note'&gt;&lt;strong&gt;Note:&lt;/strong&gt; Every MySQL statement must end in a semi-colon (;). Check to make sure this is present if you are running into any issues.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;Next, we are going to create a separate MySQL user account that we will use exclusively to operate on our new database. Creating one-function databases and accounts is a good idea from a management and security standpoint. We will use the name &lt;code&gt;wordpressuser&lt;/code&gt; in this guide. Feel free to change this if you'd like.&lt;/p&gt;

&lt;p&gt;We are going to create this account, set a password, and grant access to the database we created. We can do this by typing the following command. Remember to choose a strong password here for your database user:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;GRANT ALL ON &lt;span class="highlight"&gt;your_domain&lt;/span&gt;.* TO '&lt;span class="highlight"&gt;wordpressuser&lt;/span&gt;'@'localhost' IDENTIFIED BY '&lt;span class="highlight"&gt;password&lt;/span&gt;';
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You now have a database and user account, each made specifically for WordPress. We need to flush the privileges so that the current instance of MySQL knows about the recent changes we've made:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;FLUSH PRIVILEGES;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Exit out of MySQL by typing:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;EXIT;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The MySQL session will exit, returning you to the regular Linux shell.&lt;/p&gt;

&lt;h2 id="step-2-—-installing-additional-php-extensions"&gt;Step 2 — Installing Additional PHP Extensions&lt;/h2&gt;

&lt;p&gt;When setting up our LEMP stack, we only required a very minimal set of extensions in order to get PHP to communicate with MySQL. WordPress and many of its plugins leverage additional PHP extensions.&lt;/p&gt;

&lt;p&gt;We can download and install some of the most popular PHP extensions for use with WordPress by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt update
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo apt install php-curl php-gd php-intl php-mbstring php-soap php-xml php-xmlrpc php-zip
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span class='note'&gt;&lt;strong&gt;Note:&lt;/strong&gt; Each WordPress plugin has its own set of requirements. Some may require additional PHP packages to be installed. Check your plugin documentation to discover its PHP requirements. If they are available, they can be installed with &lt;code&gt;apt&lt;/code&gt; as demonstrated above.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;When you are finished installing the extensions, restart the PHP-FPM process so that the running PHP processor can leverage the newly installed features:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl restart php7.0-fpm
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We now have all of the necessary PHP extensions installed on the server.&lt;/p&gt;

&lt;h2 id="step-3-—-configuring-nginx"&gt;Step 3 — Configuring Nginx&lt;/h2&gt;

&lt;p&gt;Next, we will be making a few minor adjustments to our Nginx server block files. Based on the prerequisite tutorials, you should have a configuration file for your site in the &lt;code&gt;/etc/nginx/sites-available/&lt;/code&gt; directory configured to respond to your server's domain name and protected by a TLS/SSL certificate. We'll use &lt;code&gt;/etc/nginx/sites-available/&lt;span class="highlight"&gt;your_domain&lt;/span&gt;&lt;/code&gt; as an example here, but you should substitute the path to your configuration file where appropriate.&lt;/p&gt;

&lt;p&gt;Additionally, we will use &lt;code&gt;/var/www/&lt;span class="highlight"&gt;your_domain&lt;/span&gt;&lt;/code&gt; as the root directory of our WordPress install. You should use the web root specified in your own configuration.&lt;/p&gt;

&lt;p&gt;&lt;span class='note'&gt;&lt;strong&gt;Note:&lt;/strong&gt; It's possible you are using the &lt;code&gt;/etc/nginx/sites-available/default&lt;/code&gt; default configuration (with &lt;code&gt;/var/www/html&lt;/code&gt; as your web root). This is fine to use if you're only going to host one website on this server. If not, it's best to split the necessary configuration into logical chunks, one file per site.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;Open your site's Nginx configuration file with &lt;code&gt;sudo&lt;/code&gt; privileges to begin:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/nginx/sites-available/&lt;span class="highlight"&gt;your_domain&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We need to add a few &lt;code&gt;location&lt;/code&gt; directives within our main &lt;code&gt;server&lt;/code&gt; block. After adding SSL certificates your config may have &lt;em&gt;two&lt;/em&gt; &lt;code&gt;server&lt;/code&gt; blocks. If so, find the one that contains &lt;code&gt;root /var/www/&lt;span class="highlight"&gt;your_domain&lt;/span&gt;&lt;/code&gt; and your other &lt;code&gt;location&lt;/code&gt; directives and implement your changes there.&lt;/p&gt;

&lt;p&gt;Start by creating exact-matching location blocks for requests to &lt;code&gt;/favicon.ico&lt;/code&gt; and &lt;code&gt;/robots.txt&lt;/code&gt;, both of which we do not want to log requests for.&lt;/p&gt;

&lt;p&gt;We will use a regular expression location to match any requests for static files. We will again turn off the logging for these requests and will mark them as highly cacheable since these are typically expensive resources to serve. You can adjust this static files list to contain any other file extensions your site may use:&lt;/p&gt;
&lt;div class="code-label " title="/etc/nginx/sites-available/your_domain"&gt;/etc/nginx/sites-available/your_domain&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;server {
    . . .

    &lt;span class="highlight"&gt;location = /favicon.ico { log_not_found off; access_log off; }&lt;/span&gt;
    &lt;span class="highlight"&gt;location = /robots.txt { log_not_found off; access_log off; allow all; }&lt;/span&gt;
    &lt;span class="highlight"&gt;location ~* \.(css|gif|ico|jpeg|jpg|js|png)$ {&lt;/span&gt;
        &lt;span class="highlight"&gt;expires max;&lt;/span&gt;
        &lt;span class="highlight"&gt;log_not_found off;&lt;/span&gt;
    &lt;span class="highlight"&gt;}&lt;/span&gt;
    . . .
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Inside of the existing &lt;code&gt;location /&lt;/code&gt; block, we need to adjust the &lt;code&gt;try_files&lt;/code&gt; list so that instead of returning a 404 error as the default option, control is passed to the &lt;code&gt;index.php&lt;/code&gt; file with the request arguments.&lt;/p&gt;

&lt;p&gt;This should look something like this:&lt;/p&gt;
&lt;div class="code-label " title="/etc/nginx/sites-available/wordpress"&gt;/etc/nginx/sites-available/wordpress&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;server {
    . . .
    location / {
        &lt;span class="highlight"&gt;#&lt;/span&gt;try_files $uri $uri/ =404;
        &lt;span class="highlight"&gt;try_files $uri $uri/ /index.php$is_args$args;&lt;/span&gt;
    }
    . . .
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When you are finished, save and close the file.&lt;/p&gt;

&lt;p&gt;Now, we can check our configuration for syntax errors by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nginx -t
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If no errors were reported, reload Nginx by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl reload nginx
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, we will download and set up WordPress itself.&lt;/p&gt;

&lt;h2 id="step-4-—-downloading-wordpress"&gt;Step 4 — Downloading WordPress&lt;/h2&gt;

&lt;p&gt;Now that our server software is configured, we can download and set up WordPress. For security reasons in particular, it is always recommended to get the latest version of WordPress from their site.&lt;/p&gt;

&lt;p&gt;Change into a writable directory and then download the compressed release by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cd /tmp
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;curl -LO https://wordpress.org/latest.tar.gz
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Extract the compressed file to create the WordPress directory structure:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;tar xzvf latest.tar.gz
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We will be moving these files into our document root momentarily. Before we do that, we can copy over the sample configuration file to the filename that WordPress actually reads:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cp /tmp/wordpress/wp-config-sample.php /tmp/wordpress/wp-config.php
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, we can copy the entire contents of the directory into our document root. We are using the &lt;code&gt;-a&lt;/code&gt; flag to make sure our permissions are maintained. We are using a dot at the end of our source directory to indicate that everything within the directory should be copied, including any hidden files:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo cp -a /tmp/wordpress/. /var/www/&lt;span class="highlight"&gt;your_domain&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that our files are in place, we'll assign ownership them to the &lt;code&gt;www-data&lt;/code&gt; user and group. This is the user and group that Nginx runs as, and Nginx will need to be able to read and write WordPress files in order to serve the website and perform automatic updates.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo chown -R www-data:www-data /var/www/&lt;span class="highlight"&gt;your_domain&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Our files are now in our server's document root and have the correct ownership, but we still need to complete some more configuration.&lt;/p&gt;

&lt;h2 id="step-5-—-setting-up-the-wordpress-configuration-file"&gt;Step 5 — Setting up the WordPress Configuration File&lt;/h2&gt;

&lt;p&gt;Next, we need to make some changes to the main WordPress configuration file.&lt;/p&gt;

&lt;p&gt;When we open the file, our first order of business will be to adjust some secret keys to provide some security for our installation. WordPress provides a secure generator for these values so that you do not have to try to come up with good values on your own. These are only used internally, so it won't hurt usability to have complex, secure values here.&lt;/p&gt;

&lt;p&gt;To grab secure values from the WordPress secret key generator, type:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;curl -s https://api.wordpress.org/secret-key/1.1/salt/
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will get back unique values that look something like this:&lt;/p&gt;

&lt;p&gt;&lt;span class='warning'&gt;&lt;strong&gt;Warning:&lt;/strong&gt; It is important that you request unique values each time. Do &lt;strong&gt;NOT&lt;/strong&gt; copy the values shown below!&lt;br&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;define('AUTH_KEY',         '1jl/vqfs&amp;lt;XhdXoAPz9 &lt;span class="highlight"&gt;DO NOT COPY THESE VALUES&lt;/span&gt; c_j{iwqD^&amp;lt;+c9.k&amp;lt;J@4H');
define('SECURE_AUTH_KEY',  'E2N-h2]Dcvp+aS/p7X &lt;span class="highlight"&gt;DO NOT COPY THESE VALUES&lt;/span&gt; {Ka(f;rv?Pxf})CgLi-3');
define('LOGGED_IN_KEY',    'W(50,{W^,OPB%PB&amp;lt;JF &lt;span class="highlight"&gt;DO NOT COPY THESE VALUES&lt;/span&gt; 2;y&amp;amp;,2m%3]R6DUth[;88');
define('NONCE_KEY',        'll,4UC)7ua+8&amp;lt;!4VM+ &lt;span class="highlight"&gt;DO NOT COPY THESE VALUES&lt;/span&gt; #`DXF+[$atzM7 o^-C7g');
define('AUTH_SALT',        'koMrurzOA+|L_lG}kf &lt;span class="highlight"&gt;DO NOT COPY THESE VALUES&lt;/span&gt;  07VC*Lj*lD&amp;amp;?3w!BT#-');
define('SECURE_AUTH_SALT', 'p32*p,]z%LZ+pAu:VY &lt;span class="highlight"&gt;DO NOT COPY THESE VALUES&lt;/span&gt; C-?y+K0DK_+F|0h{!_xY');
define('LOGGED_IN_SALT',   'i^/G2W7!-1H2OQ+t$3 &lt;span class="highlight"&gt;DO NOT COPY THESE VALUES&lt;/span&gt; t6**bRVFSD[Hi])-qS`|');
define('NONCE_SALT',       'Q6]U:K?j4L%Z]}h^q7 &lt;span class="highlight"&gt;DO NOT COPY THESE VALUES&lt;/span&gt; 1% ^qUswWgn+6&amp;amp;xqHN&amp;amp;%');
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These are configuration lines that we can paste directly in our configuration file to set secure keys. Copy the output you received now.&lt;/p&gt;

&lt;p&gt;Now, open the WordPress configuration file:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /var/www/&lt;span class="highlight"&gt;your_domain&lt;/span&gt;/wp-config.php
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Find the section that contains the dummy values for those settings. It will look something like this:&lt;/p&gt;
&lt;div class="code-label " title="/var/www/wordpress/wp-config.php"&gt;/var/www/wordpress/wp-config.php&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .

define('AUTH_KEY',         'put your unique phrase here');
define('SECURE_AUTH_KEY',  'put your unique phrase here');
define('LOGGED_IN_KEY',    'put your unique phrase here');
define('NONCE_KEY',        'put your unique phrase here');
define('AUTH_SALT',        'put your unique phrase here');
define('SECURE_AUTH_SALT', 'put your unique phrase here');
define('LOGGED_IN_SALT',   'put your unique phrase here');
define('NONCE_SALT',       'put your unique phrase here');

. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Delete those lines and paste in the values you copied from the command line:&lt;/p&gt;
&lt;div class="code-label " title="/var/www/wordpress/wp-config.php"&gt;/var/www/wordpress/wp-config.php&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .

define('AUTH_KEY',         '&lt;span class="highlight"&gt;VALUES COPIED FROM THE COMMAND LINE&lt;/span&gt;');
define('SECURE_AUTH_KEY',  '&lt;span class="highlight"&gt;VALUES COPIED FROM THE COMMAND LINE&lt;/span&gt;');
define('LOGGED_IN_KEY',    '&lt;span class="highlight"&gt;VALUES COPIED FROM THE COMMAND LINE&lt;/span&gt;');
define('NONCE_KEY',        '&lt;span class="highlight"&gt;VALUES COPIED FROM THE COMMAND LINE&lt;/span&gt;');
define('AUTH_SALT',        '&lt;span class="highlight"&gt;VALUES COPIED FROM THE COMMAND LINE&lt;/span&gt;');
define('SECURE_AUTH_SALT', '&lt;span class="highlight"&gt;VALUES COPIED FROM THE COMMAND LINE&lt;/span&gt;');
define('LOGGED_IN_SALT',   '&lt;span class="highlight"&gt;VALUES COPIED FROM THE COMMAND LINE&lt;/span&gt;');
define('NONCE_SALT',       '&lt;span class="highlight"&gt;VALUES COPIED FROM THE COMMAND LINE&lt;/span&gt;');

. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, we need to modify some of the database connection settings at the beginning of the file. You need to adjust the database name, the database user, and the associated password that we configured within MySQL.&lt;/p&gt;

&lt;p&gt;The other change we need to make is to set the method that WordPress should use to write to the filesystem. Since we've given the web server permission to write where it needs to, we can explicitly set the filesystem method to "direct". Failure to set this with our current settings would result in WordPress prompting for FTP credentials when we perform some actions. This setting can be added below the database connection settings, or anywhere else in the file:&lt;/p&gt;
&lt;div class="code-label " title="/var/www/wordpress/wp-config.php"&gt;/var/www/wordpress/wp-config.php&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .

define('DB_NAME', '&lt;span class="highlight"&gt;your_domain&lt;/span&gt;');

/** MySQL database username */
define('DB_USER', '&lt;span class="highlight"&gt;wordpressuser&lt;/span&gt;');

/** MySQL database password */
define('DB_PASSWORD', '&lt;span class="highlight"&gt;password&lt;/span&gt;');

. . .

&lt;span class="highlight"&gt;define('FS_METHOD', 'direct');&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save and close the file when you are finished.&lt;/p&gt;

&lt;h2 id="step-6-—-completing-the-installation-through-the-web-interface"&gt;Step 6 — Completing the Installation Through the Web Interface&lt;/h2&gt;

&lt;p&gt;Now that the server configuration is complete, we can finish up the installation through the web interface.&lt;/p&gt;

&lt;p&gt;In your web browser, navigate to your server's domain name or public IP address:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;http://&lt;span class="highlight"&gt;server_domain_or_IP&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Select the language you would like to use:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/wordpress_lemp_1604/language_selection.png" alt="WordPress language selection"&gt;&lt;/p&gt;

&lt;p&gt;Next, you will come to the main setup page.&lt;/p&gt;

&lt;p&gt;Select a name for your WordPress site and choose a username (it is recommended not to choose something like "admin" for security purposes). A strong password is generated automatically. Save this password or select an alternative strong password.&lt;/p&gt;

&lt;p&gt;Enter your email address and select whether you want to discourage search engines from indexing your site:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/wordpress_lemp_1604/setup_installation.png" alt="WordPress setup installation"&gt;&lt;/p&gt;

&lt;p&gt;When you click ahead, you will be taken to a page that prompts you to log in:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/wordpress_lemp_1604/login_prompt.png" alt="WordPress login prompt"&gt;&lt;/p&gt;

&lt;p&gt;Once you log in, you will be taken to the WordPress administration dashboard:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/wordpress_lemp_1604/admin_screen.png" alt="WordPress login prompt"&gt;&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;WordPress should be installed and ready to use! Some common next steps are to choose the permalinks setting for your posts (can be found in &lt;code&gt;Settings &amp;gt; Permalinks&lt;/code&gt;) or to select a new theme (in &lt;code&gt;Appearance &amp;gt; Themes&lt;/code&gt;). If this is your first time using WordPress, explore the interface a bit to get acquainted with your new CMS.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-install-linux-nginx-mysql-php-lemp-stack-on-debian-9</id>
    <published>2018-09-13T19:52:25Z</published>
    <updated>2018-09-13T19:54:53Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-install-linux-nginx-mysql-php-lemp-stack-on-debian-9"/>
    <title>How To Install Linux, Nginx, MySQL, PHP (LEMP stack) on Debian 9</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;The LEMP software stack is a group of software that can be used to serve dynamic web pages and web applications. This is an acronym that describes a Linux operating system, with an Nginx web server. The backend data is stored in the MySQL database and the dynamic processing is handled by PHP.&lt;/p&gt;

&lt;p&gt;In this guide, you'll install a LEMP stack on a Debian server using the packages provided by the operating system.&lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;To complete this guide, you will need a Debian 9 server with a non-root user with &lt;code&gt;sudo&lt;/code&gt; privileges. You can set up a user with these privileges in our &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-debian-9"&gt;Initial Server Setup with Debian 9&lt;/a&gt; guide.&lt;/p&gt;

&lt;h2 id="step-1-—-installing-the-nginx-web-server"&gt;Step 1 — Installing the Nginx Web Server&lt;/h2&gt;

&lt;p&gt;In order to display web pages to our site visitors, we are going to employ Nginx, a modern, efficient web server.&lt;/p&gt;

&lt;p&gt;All of the software we will be using for this procedure will come directly from Debian's default package repositories. This means we can use the &lt;code&gt;apt&lt;/code&gt; package management suite to complete the installation.&lt;/p&gt;

&lt;p&gt;Since this is our first time using &lt;code&gt;apt&lt;/code&gt; for this session, we should start off by updating our local package index. We can then install the server:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt update
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo apt install nginx
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On Debian 9, Nginx is configured to start running upon installation.&lt;/p&gt;

&lt;p&gt;If you have the &lt;code&gt;ufw&lt;/code&gt; firewall running, you will need to allow connections to Nginx. You should enable the most restrictive profile that will still allow the traffic you want. Since we haven't configured SSL for our server yet, in this guide, we will only need to allow traffic on port &lt;code&gt;80&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can enable this by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw allow 'Nginx HTTP'
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can verify the change by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw status
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see HTTP traffic allowed in the displayed output:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Status: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere
Nginx HTTP                 ALLOW       Anywhere
OpenSSH (v6)               ALLOW       Anywhere (v6)
Nginx HTTP (v6)            ALLOW       Anywhere (v6)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, test if the server is up and running by accessing your server's domain name or public IP address in your web browser. If you do not have a domain name pointed at your server and you do not know your server's public IP address, you can find it by typing one of the following into your terminal:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;ip addr show eth0 | grep inet | awk '{ print $2; }' | sed 's/\/.*$//'
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will print out a few IP addresses. You can try each of them in turn in your web browser.&lt;/p&gt;

&lt;p&gt;Type one of the addresses that you receive in your web browser. It should take you to Nginx's default landing page:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;http://&lt;span class="highlight"&gt;your_domain_or_IP&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/lemp_debian8/THcJfIl.png" alt="Nginx default page"&gt;&lt;/p&gt;

&lt;p&gt;If you see the above page, you have successfully installed Nginx.&lt;/p&gt;

&lt;h2 id="step-2-—-installing-mysql-to-manage-site-data"&gt;Step 2 — Installing MySQL to Manage Site Data&lt;/h2&gt;

&lt;p&gt;Now that we have a web server, we need to install MySQL, a database management system, to store and manage the data for our site.&lt;/p&gt;

&lt;p&gt;You can install this easily by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install mysql-server
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span class='note'&gt;&lt;strong&gt;Note:&lt;/strong&gt; In Debian 9 a community fork of the MySQL project – MariaDB – is packaged as the default MySQL variant. While, MariaDB works well in most cases, if you need features found only in Oracle's MySQL, you can install and use packages from a repository maintained by the MySQL developers. To install the official MySQL server, use our tutorial &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-the-latest-mysql-on-debian-9"&gt;&lt;em&gt;How To Install the Latest MySQL on Debian 9&lt;/em&gt;&lt;/a&gt;.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;The MySQL database software is now installed, but its configuration is not complete.&lt;/p&gt;

&lt;p&gt;To secure the installation, we can run a security script that will ask whether we want to modify some insecure defaults. Begin the script by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo mysql_secure_installation
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will be asked to enter the password for the MySQL &lt;strong&gt;root&lt;/strong&gt; account. We haven't set this yet, so just hit &lt;code&gt;ENTER&lt;/code&gt;. Then you'll be asked you if you want to set that password. You should type &lt;code&gt;y&lt;/code&gt; then set a &lt;strong&gt;root&lt;/strong&gt; password.&lt;/p&gt;

&lt;p&gt;For the rest of the questions the script asks, you should press &lt;code&gt;y&lt;/code&gt;, followed by the &lt;code&gt;ENTER&lt;/code&gt; key at each prompt. This will remove some anonymous users and the test database, disable remote root logins, and load these new rules so that MySQL immediately respects the changes you have made.&lt;/p&gt;

&lt;p&gt;At this point, your database system is now set up and secured. Let's set up PHP.&lt;/p&gt;

&lt;h2 id="step-3-—-installing-php-for-processing"&gt;Step 3 — Installing PHP for Processing&lt;/h2&gt;

&lt;p&gt;We now have Nginx installed to serve our pages and MySQL installed to store and manage our data. However, we still don't have anything that can generate dynamic content. That's where PHP comes in.&lt;/p&gt;

&lt;p&gt;Since Nginx does not contain native PHP processing like some other web servers, we will need to install &lt;code&gt;fpm&lt;/code&gt;, which stands for "fastCGI process manager". We will tell Nginx to pass PHP requests to this software for processing. We'll also install an additional helper package that will allow PHP to communicate with our MySQL database backend. The installation will pull in the necessary PHP core files to make that work.&lt;/p&gt;

&lt;p&gt;Then install the &lt;code&gt;php-fpm&lt;/code&gt; and &lt;code&gt;php-mysql&lt;/code&gt; packages:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install php-fpm php-mysql
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We now have our PHP components installed. Next we'll configure Nginx to use them.&lt;/p&gt;

&lt;h2 id="step-4-—-configuring-nginx-to-use-the-php-processor"&gt;Step 4 — Configuring Nginx to Use the PHP Processor&lt;/h2&gt;

&lt;p&gt;Now we have all of the required components installed. The only configuration change we still need is to tell Nginx to use our PHP processor for dynamic content.&lt;/p&gt;

&lt;p&gt;We do this on the server block level (server blocks are similar to Apache's virtual hosts). We're going to leave the default Nginx configuration alone and instead create a new configuration file and new web root directory to hold our PHP files. We'll name the configuration file and the directory after the domain name or hostname that the server should respond to.&lt;/p&gt;

&lt;p&gt;First, create a new directory in &lt;code&gt;/var/www&lt;/code&gt; to hold the PHP site:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo mkdir /var/www/&lt;span class="highlight"&gt;your_domain&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, open a new configuration file in Nginx's &lt;code&gt;sites-available&lt;/code&gt; directory:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/nginx/sites-available/&lt;span class="highlight"&gt;your_domain&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will create a new blank file. Paste in the following bare-bones configuration:&lt;/p&gt;
&lt;div class="code-label " title="/etc/nginx/sites-available/your_domain"&gt;/etc/nginx/sites-available/your_domain&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;server {
    listen 80;
    listen [::]:80;

    root /var/www/&lt;span class="highlight"&gt;your_domain&lt;/span&gt;;
    index index.php index.html index.htm;

    server_name &lt;span class="highlight"&gt;your_domain&lt;/span&gt;;

    location / {
        try_files $uri $uri/ =404;
    }

    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is a very basic configuration that listens on port 80 and serves files from the web root we just created. It will only respond to requests to the name provided after &lt;code&gt;server_name&lt;/code&gt;, and any files ending in &lt;code&gt;.php&lt;/code&gt; will be processed by the &lt;code&gt;php-fpm&lt;/code&gt; process before Nginx sends the results to the user.&lt;/p&gt;

&lt;p&gt;Save and close the file when you're done customizing it.&lt;/p&gt;

&lt;p&gt;Activate your configuration by linking to the config file from Nginx's &lt;code&gt;sites-enabled&lt;/code&gt; directory:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ln -s /etc/nginx/sites-available/&lt;span class="highlight"&gt;your_domain&lt;/span&gt;.conf /etc/nginx/sites-enabled/
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will tell Nginx to use the configuration next time it is reloaded. First, test your configuration for syntax errors by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nginx -t
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If any errors are reported, go back and recheck your file before continuing.&lt;/p&gt;

&lt;p&gt;When you are ready, reload Nginx to make the changes:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl reload nginx
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next we'll create a file in our new web root directory to test out PHP processing.&lt;/p&gt;

&lt;h2 id="step-5-—-create-a-php-file-to-test-configuration"&gt;Step 5 — Create a PHP File to Test Configuration&lt;/h2&gt;

&lt;p&gt;Your LEMP stack should now be completely set up. We can test it to validate that Nginx can correctly hand &lt;code&gt;.php&lt;/code&gt; files off to our PHP processor.&lt;/p&gt;

&lt;p&gt;We can do this by creating a test PHP file in our document root. Open a new file called &lt;code&gt;info.php&lt;/code&gt; within your document root in your text editor:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /var/www/&lt;span class="highlight"&gt;your_domain&lt;/span&gt;/info.php
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Type or paste the following lines into the new file. This is valid PHP code that will return information about our server:&lt;/p&gt;
&lt;div class="code-label " title="/var/www/your_domain/info.php"&gt;/var/www/your_domain/info.php&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&amp;lt;?php
  phpinfo();
?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When you are finished, save and close the file.&lt;/p&gt;

&lt;p&gt;Now, you can visit this page in your web browser by visiting your server's domain name or public IP address followed by &lt;code&gt;/info.php&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;http://&lt;span class="highlight"&gt;your_domain&lt;/span&gt;/info.php
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see a web page that has been generated by PHP with information about your server:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/lemp_debian8/ZP3DpyX.png" alt="PHP page info"&gt;&lt;/p&gt;

&lt;p&gt;If you see a page that looks like this, you've set up PHP processing with Nginx successfully.&lt;/p&gt;

&lt;p&gt;After verifying that Nginx renders the page correctly, it's best to remove the file you created as it can actually give unauthorized users some hints about your configuration that may help them try to break in.&lt;/p&gt;

&lt;p&gt;For now, remove the file by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo rm /var/www/html/info.php
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can always regenerate this file if you need it later.&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;You should now have a LEMP stack configured on your Debian server. This gives you a very flexible foundation for serving web content to your visitors.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/modernizing-applications-for-kubernetes</id>
    <published>2018-09-11T22:07:45Z</published>
    <updated>2018-09-19T18:14:47Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/modernizing-applications-for-kubernetes"/>
    <title>Modernizing Applications for Kubernetes</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;Modern stateless applications are built and designed to run in software containers like Docker, and be managed by container clusters like Kubernetes. They are developed using &lt;a href="https://github.com/cncf/toc/blob/master/DEFINITION.md"&gt;Cloud Native&lt;/a&gt; and &lt;a href="https://12factor.net/"&gt;Twelve Factor&lt;/a&gt; principles and patterns, to minimize manual intervention and maximize portability and redundancy. Migrating virtual-machine or bare metal-based applications into containers (known as "containerizing") and deploying them inside of clusters often involves significant shifts in how these apps are built, packaged, and delivered.&lt;/p&gt;

&lt;p&gt;Building on &lt;a href="https://www.digitalocean.com/community/tutorials/architecting-applications-for-kubernetes"&gt;Architecting Applications for Kubernetes&lt;/a&gt;, in this conceptual guide, we'll discuss high-level steps for modernizing your applications, with the end goal of running and managing them in a Kubernetes cluster. Although you can run stateful applications like databases on Kubernetes, this guide focuses on migrating and modernizing stateless applications, with persistent data offloaded to an external data store. Kubernetes provides advanced functionality for efficiently managing and scaling stateless applications, and we'll explore the application and infrastructure changes necessary for running scalable, observable, and portable apps on Kubernetes. &lt;/p&gt;

&lt;h2 id="preparing-the-application-for-migration"&gt;Preparing the Application for Migration&lt;/h2&gt;

&lt;p&gt;Before containerizing your application or writing Kubernetes Pod and Deployment configuration files, you should implement application-level changes to maximize your app's portability and observability in Kubernetes. Kubernetes is a highly automated environment that can automatically deploy and restart failing application containers, so it's important to build in the appropriate application logic to communicate with the container orchestrator and allow it to automatically scale your app as necessary. &lt;/p&gt;

&lt;h3 id="extract-configuration-data"&gt;Extract Configuration Data&lt;/h3&gt;

&lt;p&gt;One of the first application-level changes to implement is extracting application configuration from application code. Configuration consists of any information that varies across deployments and environments, like service endpoints, database addresses, credentials, and various parameters and options. For example, if you have two environments, say &lt;code&gt;staging&lt;/code&gt; and &lt;code&gt;production&lt;/code&gt;, and each contains a separate database, your application should not have the database endpoint and credentials explicitly declared in the code, but stored in a separate location, either as variables in the running environment, a local file, or external key-value store, from which the values are read into the app. &lt;/p&gt;

&lt;p&gt;Hardcoding these parameters into your code poses a security risk as this config data often consists of sensitive information, which you then check in to your version control system. It also increases complexity as you now have to maintain multiple versions of your application, each consisting of the same core application logic, but varying slightly in configuration. As applications and their configuration data grow, hardcoding config into app code quickly becomes unwieldy.&lt;/p&gt;

&lt;p&gt;By extracting configuration values from your application code, and instead ingesting them from the running environment or local files, your app becomes a generic, portable package that can be deployed into any environment, provided you supply it with accompanying configuration data. Container software like Docker and cluster software like Kubernetes have been designed around this paradigm, building in features for managing configuration data and injecting it into application containers. These features will be covered in more detail in the &lt;a href="https://www.digitalocean.com/community/tutorials/modernizing-applications-for-kubernetes#inject-configuration"&gt;Containerizing&lt;/a&gt; and &lt;a href="https://www.digitalocean.com/community/tutorials/modernizing-applications-for-kubernetes#injecting-configuration-data-with-kubernetes"&gt;Kubernetes&lt;/a&gt; sections.&lt;/p&gt;

&lt;p&gt;Here’s a quick example demonstrating how to externalize two config values &lt;code&gt;DB_HOST&lt;/code&gt; and &lt;code&gt;DB_USER&lt;/code&gt; from a simple Python &lt;a href="http://flask.pocoo.org/"&gt;Flask&lt;/a&gt; app’s code. We'll make them available in the app’s running environment as env vars, from which the app will read them:&lt;/p&gt;
&lt;div class="code-label " title="hardcoded_config.py"&gt;hardcoded_config.py&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;from flask import Flask

DB_HOST = 'mydb.mycloud.com'
DB_USER = 'sammy'

app = Flask(__name__)

@app.route('/')
def print_config():
    output = 'DB_HOST: {} -- DB_USER: {}'.format(DB_HOST, DB_USER)
    return output
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Running this simple app (consult the &lt;a href="http://flask.pocoo.org/docs/1.0/quickstart/"&gt;Flask Quickstart&lt;/a&gt; to learn how) and visiting its web endpoint will display a page containing these two config values.&lt;/p&gt;

&lt;p&gt;Now, here’s the same example with the config values externalized to the app’s running environment:&lt;/p&gt;
&lt;div class="code-label " title="env_config.py"&gt;env_config.py&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;span class="highlight"&gt;import os&lt;/span&gt;

from flask import Flask

DB_HOST = &lt;span class="highlight"&gt;os.environ.get('APP_DB_HOST')&lt;/span&gt;
DB_USER = &lt;span class="highlight"&gt;os.environ.get('APP_DB_USER')&lt;/span&gt;

app = Flask(__name__)

@app.route('/')
def print_config():
    output = 'DB_HOST: {} -- DB_USER: {}'.format(DB_HOST, DB_USER)
    return output
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Before running the app, we set the necessary config variables in the local environment:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;export APP_DB_HOST=mydb.mycloud.com
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;export APP_DB_USER=sammy
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;flask run
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The displayed web page should contain the same text as in the first example, but the app’s config can now be modified independently of the application code. You can use a similar approach to read in config parameters from a local file.&lt;/p&gt;

&lt;p&gt;In the next section we’ll discuss moving application state outside of containers.&lt;/p&gt;

&lt;h3 id="offload-application-state"&gt;Offload Application State&lt;/h3&gt;

&lt;p&gt;Cloud Native applications run in containers, and are dynamically orchestrated by cluster software like Kubernetes or Docker Swarm. A given app or service can be load balanced across multiple replicas, and any individual app container should be able to fail, with minimal or no disruption of service for clients. To enable this horizontal, redundant scaling, applications must be designed in a stateless fashion. This means that they respond to client requests without storing persistent client and application data locally, and at any point in time if the running app container is destroyed or restarted, critical data is not lost.&lt;/p&gt;

&lt;p&gt;For example, if you are running an address book application and your app adds, removes and modifies contacts from an address book, the address book data store should be an external database or other data store, and the only data kept in container memory should be short-term in nature, and disposable without critical loss of information. Data that persists across user visits like sessions should also be moved to external data stores like Redis. Wherever possible, you should offload any state from your app to services like managed databases or caches.&lt;/p&gt;

&lt;p&gt;For stateful applications that require a persistent data store (like a replicated MySQL database), Kubernetes builds in features for attaching persistent block storage volumes to containers and Pods. To ensure that a Pod can maintain state and access the same persistent volume after a restart, the StatefulSet workload must be used. StatefulSets are ideal for deploying databases and other long-running data stores to Kubernetes. &lt;/p&gt;

&lt;p&gt;Stateless containers enable maximum portability and full use of available cloud resources, allowing the Kubernetes scheduler to quickly scale your app up and down and launch Pods wherever resources are available. If you don’t require the stability and ordering guarantees provided by the StatefulSet workload, you should use the Deployment workload to manage and scale and your applications.&lt;/p&gt;

&lt;p&gt;To learn more about the design and architecture of stateless, Cloud Native microservices, consult our &lt;a href="http://assets.digitalocean.com/white-papers/running-digitalocean-kubernetes.pdf"&gt;Kubernetes White Paper&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id="implement-health-checks"&gt;Implement Health Checks&lt;/h3&gt;

&lt;p&gt;In the Kubernetes model, the cluster control plane can be relied on to repair a broken application or service. It does this by checking the health of application Pods, and restarting or rescheduling unhealthy or unresponsive containers. By default, if your application container is running, Kubernetes sees your Pod as "healthy." In many cases this is a reliable indicator for the health of a running application. However, if your application is deadlocked and not performing any meaningful work, the app process and container will continue to run indefinitely, and by default Kubernetes will keep the stalled container alive.&lt;/p&gt;

&lt;p&gt;To properly communicate application health to the Kubernetes control plane, you should implement custom application health checks that indicate when an application is both running and ready to receive traffic. The first type of health check is called a &lt;strong&gt;readiness probe&lt;/strong&gt;, and lets Kubernetes know when your application is ready to receive traffic. The second type of check is called a &lt;strong&gt;liveness probe&lt;/strong&gt;, and lets Kubernetes know when your application is healthy and running. The Kubelet Node agent can perform these probes on running Pods using 3 different methods:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;HTTP: The Kubelet probe performs an HTTP GET request against an endpoint (like &lt;code&gt;/health&lt;/code&gt;), and succeeds if the response status is between 200 and 399&lt;/li&gt;
&lt;li&gt;Container Command: The Kubelet probe executes a command inside of the running container. If the exit code is 0, then the probe succeeds.&lt;/li&gt;
&lt;li&gt;TCP: The Kubelet probe attempts to connect to your container on a specified port. If it can establish a TCP connection, then the probe succeeds.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You should choose the appropriate method depending on the running application(s), programming language, and framework. The readiness and liveness probes can both use the same probe method and perform the same check, but the inclusion of a readiness probe will ensure that the Pod doesn't receive traffic until the probe begins succeeding.&lt;/p&gt;

&lt;p&gt;When planning and thinking about containerizing your application and running it on Kubernetes, you should allocate planning time for defining what "healthy" and "ready" mean for your particular application, and development time for implementing and testing the endpoints and/or check commands.&lt;/p&gt;

&lt;p&gt;Here’s a minimal health endpoint for the Flask example referenced above:&lt;/p&gt;
&lt;div class="code-label " title="env_config.py"&gt;env_config.py&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .  
&lt;a href="https://www.digitalocean.com/community/users/app-route" class="username-tag"&gt;@app.route&lt;/a&gt;('/')
def print_config():
    output = 'DB_HOST: {} -- DB_USER: {}'.format(DB_HOST, DB_USER)
    return output

&lt;span class="highlight"&gt;@app.route('/health')&lt;/span&gt;
&lt;span class="highlight"&gt;def return_ok():&lt;/span&gt;
&lt;span class="highlight"&gt;    return 'Ok!', 200&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A Kubernetes liveness probe that checks this path would then look something like this:&lt;/p&gt;
&lt;div class="code-label " title="pod_spec.yaml"&gt;pod_spec.yaml&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .
  livenessProbe:
      httpGet:
        path: /health
        port: 80
      initialDelaySeconds: 5
      periodSeconds: 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;initialDelaySeconds&lt;/code&gt; field specifies that Kubernetes (specifically the Node Kubelet) should probe the &lt;code&gt;/health&lt;/code&gt; endpoint after waiting 5 seconds, and &lt;code&gt;periodSeconds&lt;/code&gt; tells the Kubelet to probe &lt;code&gt;/health&lt;/code&gt; every 2 seconds. &lt;/p&gt;

&lt;p&gt;To learn more about liveness and readiness probes, consult the &lt;a href="https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/"&gt;Kubernetes documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id="instrument-code-for-logging-and-monitoring"&gt;Instrument Code for Logging and Monitoring&lt;/h3&gt;

&lt;p&gt;When running your containerized application in an environment like Kubernetes, it's important to publish telemetry and logging data to monitor and debug your application's performance. Building in features to publish performance metrics like response duration and error rates will help you monitor your application and alert you when your application is unhealthy.&lt;/p&gt;

&lt;p&gt;One tool you can use to monitor your services is &lt;a href="https://prometheus.io/"&gt;Prometheus&lt;/a&gt;, an open-source systems monitoring and alerting toolkit, hosted by the Cloud Native Computing Foundation (CNCF). Prometheus provides several client libraries for instrumenting your code with various metric types to count events and their durations. For example, if you're using the Flask Python framework, you can use the Prometheus &lt;a href="https://github.com/prometheus/client_python"&gt;Python client&lt;/a&gt; to add decorators to your request processing functions to track the time spent processing requests. These metrics can then be scraped by Prometheus at an HTTP endpoint like &lt;code&gt;/metrics&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A helpful method to use when designing your app's instrumentation is the RED method. It consists of the following three key request metrics:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rate: The number of requests received by your application&lt;/li&gt;
&lt;li&gt;Errors: The number of errors emitted by your application&lt;/li&gt;
&lt;li&gt;Duration: The amount of time it takes your application to serve a response&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This minimal set of metrics should give you enough data to alert on when your application's performance degrades. Implementing this instrumentation along with the health checks discussed above will allow you to quickly detect and recover from a failing application.&lt;/p&gt;

&lt;p&gt;To learn more about signals to measure when monitoring your applications, consult &lt;a href="https://landing.google.com/sre/book/chapters/monitoring-distributed-systems.html#xref_monitoring_golden-signals"&gt;Monitoring Distributed Systems&lt;/a&gt; from the Google Site Reliability Engineering book.&lt;/p&gt;

&lt;p&gt;In addition to thinking about and designing features for publishing telemetry data, you should also plan  how your application will log in a distributed cluster-based environment. You should ideally remove hardcoded configuration references to local log files and log directories, and instead log directly to stdout and stderr. You should treat logs as a continuous event stream, or sequence of time-ordered events. This output stream will then get captured by the container enveloping your application, from which it can be forwarded to a logging layer like the EFK (Elasticsearch, Fluentd, and Kibana) stack. Kubernetes provides a lot of flexibility in designing your logging architecture, which we'll explore in more detail below.&lt;/p&gt;

&lt;h3 id="build-administration-logic-into-api"&gt;Build Administration Logic into API&lt;/h3&gt;

&lt;p&gt;Once your application is containerized and up and running in a cluster environment like Kubernetes, you may no longer have shell access to the container running your app. If you've implemented adequate health checking, logging, and monitoring, you can quickly be alerted on, and debug production issues, but taking action beyond restarting and redeploying containers may be difficult. For quick operational and maintenance fixes like flushing queues or clearing a cache, you should implement the appropriate API endpoints so that you can perform these operations without having to restart containers or &lt;code&gt;exec&lt;/code&gt; into running containers and execute series of commands. Containers should be treated as immutable objects, and manual administration should be avoided in a production environment. If you must perform one-off administrative tasks, like clearing caches, you should expose this functionality via the API.&lt;/p&gt;

&lt;h3 id="summary"&gt;Summary&lt;/h3&gt;

&lt;p&gt;In these sections we’ve discussed application-level changes you may wish to implement before containerizing your application and moving it to Kubernetes. For a more in-depth walkthrough on building Cloud Native apps, consult &lt;a href="https://www.digitalocean.com/community/tutorials/architecting-applications-for-kubernetes"&gt;Architecting Applications for Kubernetes&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We’ll now discuss some considerations to keep in mind when building containers for your apps.&lt;/p&gt;

&lt;h2 id="containerizing-your-application"&gt;Containerizing Your Application&lt;/h2&gt;

&lt;p&gt;Now that you've implemented app logic to maximize its portability and observability in a cloud-based environment, it's time to package your app inside of a container. For the purposes of this guide, we'll use Docker containers, but you should use whichever container implementation best suits your production needs.&lt;/p&gt;

&lt;h3 id="explicitly-declare-dependencies"&gt;Explicitly Declare Dependencies&lt;/h3&gt;

&lt;p&gt;Before creating a Dockerfile for your application, one of the first steps is taking stock of the software and operating system dependencies your application needs to run correctly. Dockerfiles allow you to explicitly version every piece of software installed into the image, and you should take advantage of this feature by explicitly declaring the parent image, software library, and programming language versions. &lt;/p&gt;

&lt;p&gt;Avoid &lt;code&gt;latest&lt;/code&gt; tags and unversioned packages as much as possible, as these can shift, potentially breaking your application. You may wish to create a private registry or private mirror of a public registry to exert more control over image versioning and to prevent upstream changes from unintentionally breaking your image builds. &lt;/p&gt;

&lt;p&gt;To learn more about setting up a private image registry, consult &lt;a href="https://docs.docker.com/registry/deploying/"&gt;Deploy a Registry Server&lt;/a&gt; from the Docker official documentation and the &lt;a href="https://www.digitalocean.com/community/tutorials/modernizing-applications-for-kubernetes#publish-image-to-a-registry"&gt;Registries&lt;/a&gt; section below.&lt;/p&gt;

&lt;h3 id="keep-image-sizes-small"&gt;Keep Image Sizes Small&lt;/h3&gt;

&lt;p&gt;When deploying and pulling container images, large images can significantly slow things down and add to your bandwidth costs. Packaging a minimal set of tools and application files into an image provides several benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reduce image sizes&lt;/li&gt;
&lt;li&gt;Speed up image builds&lt;/li&gt;
&lt;li&gt;Reduce container start lag &lt;/li&gt;
&lt;li&gt;Speed up image transfer times&lt;/li&gt;
&lt;li&gt;Improve security by reducing attack surface &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some steps you can consider when building your images:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use a minimal base OS image like &lt;code&gt;alpine&lt;/code&gt; or build from &lt;code&gt;scratch&lt;/code&gt; instead of a fully featured OS like &lt;code&gt;ubuntu&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Clean up unnecessary files and artifacts after installing software&lt;/li&gt;
&lt;li&gt;Use separate "build" and "runtime" containers to keep production application containers small&lt;/li&gt;
&lt;li&gt;Ignore unnecessary build artifacts and files when copying in large directories&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a full guide on optimizing Docker containers, including many illustrative examples, consult &lt;a href="https://www.digitalocean.com/community/tutorials/building-optimized-containers-for-kubernetes"&gt;Building Optimized Containers for Kubernetes&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id="inject-configuration"&gt;Inject Configuration&lt;/h3&gt;

&lt;p&gt;Docker provides several helpful features for injecting configuration data into your app's running environment. &lt;/p&gt;

&lt;p&gt;One option for doing this is specifying environment variables and their values in the Dockerfile using the &lt;code&gt;ENV&lt;/code&gt; statement, so that configuration data is built-in to images:&lt;/p&gt;
&lt;div class="code-label " title="Dockerfile"&gt;Dockerfile&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;...
ENV MYSQL_USER=my_db_user
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Your app can then parse these values from its running environment and configure its settings appropriately.&lt;/p&gt;

&lt;p&gt;You can also pass in environment variables as parameters when starting a container using &lt;code&gt;docker run&lt;/code&gt; and the &lt;code&gt;-e&lt;/code&gt; flag:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;docker run -e MYSQL_USER='my_db_user' IMAGE[:TAG] 
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, you can use an env file, containing a list of environment variables and their values. To do this, create the file and use the &lt;code&gt;--env-file&lt;/code&gt; parameter to pass it in to the command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;docker run --env-file var_list IMAGE[:TAG]
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you're modernizing your application to run it using a cluster manager like Kubernetes, you should further externalize your config from the image, and manage configuration using Kubernetes' built-in &lt;a href="https://kubernetes.io/docs/tasks/configure-pod-container/configure-pod-configmap/"&gt;ConfigMap&lt;/a&gt; and &lt;a href="https://kubernetes.io/docs/concepts/configuration/secret/"&gt;Secrets&lt;/a&gt; objects. This allows you to separate configuration from image manifests, so that you can manage and version it separately from your application. To learn how to externalize configuration using ConfigMaps and Secrets, consult the &lt;a href="https://www.digitalocean.com/community/tutorials/modernizing-applications-for-kubernetes#injecting-configuration-data-with-kubernetes"&gt;ConfigMaps and Secrets section&lt;/a&gt; below. &lt;/p&gt;

&lt;h3 id="publish-image-to-a-registry"&gt;Publish Image to a Registry&lt;/h3&gt;

&lt;p&gt;Once you've built your application images, to make them available to Kubernetes, you should upload them to a container image registry. Public registries like &lt;a href="https://hub.docker.com"&gt;Docker Hub&lt;/a&gt; host the latest Docker images for popular open source projects like &lt;a href="https://hub.docker.com/_/node/"&gt;Node.js&lt;/a&gt; and &lt;a href="https://hub.docker.com/_/nginx/"&gt;nginx&lt;/a&gt;. Private registries allow you publish your internal application images, making them available to developers and infrastructure, but not the wider world.&lt;/p&gt;

&lt;p&gt;You can deploy a private registry using your existing infrastructure (e.g. on top of cloud object storage), or optionally use one of several Docker registry products like &lt;a href="https://quay.io/"&gt;Quay.io&lt;/a&gt; or paid Docker Hub plans. These registries can integrate with hosted version control services like GitHub so that when a Dockerfile is updated and pushed, the registry service will automatically pull the new Dockerfile, build the container image, and make the updated image available to your services.&lt;/p&gt;

&lt;p&gt;To exert more control over the building and testing of your container images and their tagging and publishing, you can implement a continuous integration (CI) pipeline.&lt;/p&gt;

&lt;h3 id="implement-a-build-pipeline"&gt;Implement a Build Pipeline&lt;/h3&gt;

&lt;p&gt;Building, testing, publishing and deploying your images into production manually can be error-prone and does not scale well. To manage builds and continuously publish containers containing your latest code changes to your image registry, you should use a build pipeline.&lt;/p&gt;

&lt;p&gt;Most build pipelines perform the following core functions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Watch source code repositories for changes&lt;/li&gt;
&lt;li&gt;Run smoke and unit tests on modified code&lt;/li&gt;
&lt;li&gt;Build container images containing modified code&lt;/li&gt;
&lt;li&gt;Run further integration tests using built container images&lt;/li&gt;
&lt;li&gt;If tests pass, tag and publish images to registry&lt;/li&gt;
&lt;li&gt;(Optional, in continuous deployment setups) Update Kubernetes Deployments and roll out images to staging/production clusters&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are many paid continuous integration products that have built-in integrations with popular version control services like GitHub and image registries like Docker Hub. An alternative to these products is &lt;a href="https://jenkins.io/"&gt;Jenkins&lt;/a&gt;, a free and open-source build automation server that can be configured to perform all of the functions described above. To learn how to set up a Jenkins continuous integration pipeline, consult &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-set-up-continuous-integration-pipelines-in-jenkins-on-ubuntu-16-04"&gt;How To Set Up Continuous Integration Pipelines in Jenkins on Ubuntu 16.04&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id="implement-container-logging-and-monitoring"&gt;Implement Container Logging and Monitoring&lt;/h3&gt;

&lt;p&gt;When working with containers, it's important to think about the logging infrastructure you will use to manage and store logs for all your running and stopped containers. There are multiple container-level patterns you can use for logging, and also multiple Kubernetes-level patterns. &lt;/p&gt;

&lt;p&gt;In Kubernetes, by default containers use the &lt;code&gt;json-file&lt;/code&gt; Docker &lt;a href="https://docs.docker.com/config/containers/logging/configure/"&gt;logging driver&lt;/a&gt;, which captures the stdout and stderr streams and writes them to JSON files on the Node where the container is running. Sometimes logging directly to stderr and stdout may not be enough for your application container, and you may want to pair the app container with a logging &lt;em&gt;sidecar&lt;/em&gt; container in a Kubernetes Pod. This sidecar container can then pick up logs from the filesystem, a local socket, or the systemd journal, granting you a little more flexibility than simply using the stderr and stdout streams. This container can also do some processing and then stream enriched logs to stdout/stderr, or directly to a logging backend. To learn more about Kubernetes logging patterns, consult the Kubernetes logging and monitoring &lt;a href="https://www.digitalocean.com/community/tutorials/modernizing-applications-for-kubernetes#logging-and-monitoring"&gt;section&lt;/a&gt; of this tutorial.&lt;/p&gt;

&lt;p&gt;How your application logs at the container level will depend on its complexity. For simple, single-purpose microservices, logging directly to stdout/stderr and letting Kubernetes pick up these streams is the recommended approach, as you can then leverage the &lt;code&gt;kubectl logs&lt;/code&gt; command to access log streams from your Kubernetes-deployed containers.&lt;/p&gt;

&lt;p&gt;Similar to logging, you should begin thinking about monitoring in a container and cluster-based environment. Docker provides the helpful &lt;code&gt;docker stats&lt;/code&gt; command for grabbing standard metrics like CPU and memory usage for running containers on the host, and exposes even more metrics through the &lt;a href="https://docs.docker.com/develop/sdk/"&gt;Remote REST API&lt;/a&gt;. Additionally, the open-source tool &lt;a href="https://github.com/google/cadvisor"&gt;cAdvisor&lt;/a&gt; (installed on Kubernetes Nodes by default) provides more advanced functionality like historical metric collection, metric data export, and a helpful web UI for sorting through the data.&lt;/p&gt;

&lt;p&gt;However, in a multi-node, multi-container production environment, more complex metrics stacks like &lt;a href="https://prometheus.io/"&gt;Prometheus&lt;/a&gt; and &lt;a href="https://grafana.com/"&gt;Grafana&lt;/a&gt; may help organize and monitor your containers' performance data. &lt;/p&gt;

&lt;h3 id="summary"&gt;Summary&lt;/h3&gt;

&lt;p&gt;In these sections, we briefly discussed some best practices for building containers, setting up a CI/CD pipeline and image registry, as well as some considerations for increasing observability into your containers. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;To learn more about optimizing containers for Kubernetes, consult &lt;a href="https://www.digitalocean.com/community/tutorials/building-optimized-containers-for-kubernetes"&gt;Building Optimized Containers for Kubernetes&lt;/a&gt;. &lt;/li&gt;
&lt;li&gt;To learn more about CI/CD, consult &lt;a href="https://www.digitalocean.com/community/tutorials/an-introduction-to-continuous-integration-delivery-and-deployment"&gt;An Introduction to Continuous Integration, Delivery, and Deployment&lt;/a&gt; and &lt;a href="https://www.digitalocean.com/community/tutorials/an-introduction-to-ci-cd-best-practices"&gt;An Introduction to CI/CD Best Practices&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the next section, we’ll explore Kubernetes features that allow you to run and scale your containerized app in a cluster. &lt;/p&gt;

&lt;h2 id="deploying-on-kubernetes"&gt;Deploying on Kubernetes&lt;/h2&gt;

&lt;p&gt;At this point, you’ve containerized your app and implemented logic to maximize its portability and observability in Cloud Native environments. We’ll now explore Kubernetes features that provide simple interfaces for managing and scaling your apps in a Kubernetes cluster.  &lt;/p&gt;

&lt;h3 id="write-deployment-and-pod-configuration-files"&gt;Write Deployment and Pod Configuration Files&lt;/h3&gt;

&lt;p&gt;Once you've containerized your application and published it to a registry, you can now deploy it into a Kubernetes cluster using the Pod workload. The smallest deployable unit in a Kubernetes cluster is not a container but a Pod. Pods typically consist of an application container (like a containerized Flask web app), or an app container and any “sidecar” containers that perform some helper function like monitoring or logging. Containers in a Pod share storage resources, a network namespace, and port space. They can communicate with each other using &lt;code&gt;localhost&lt;/code&gt; and can share data using mounted volumes. Addtionally, the Pod workload allows you to define &lt;a href="https://kubernetes.io/docs/concepts/workloads/pods/init-containers/"&gt;Init Containers&lt;/a&gt; that run setup scripts or utilities before the main app container begins running. &lt;/p&gt;

&lt;p&gt;Pods are typically rolled out using Deployments, which are Controllers defined by YAML files that declare a particular desired state. For example, an application state could be running three replicas of the Flask web app container and exposing port 8080. Once created, the control plane gradually brings the actual state of the cluster to match the desired state declared in the Deployment by scheduling containers onto Nodes as required. To scale the number of application replicas running in the cluster, say from 3 up to 5, you update the &lt;code&gt;replicas&lt;/code&gt; field of the Deployment configuration file, and then &lt;code&gt;kubectl apply&lt;/code&gt; the new configuration file. Using these configuration files, scaling and deployment operations can all be tracked and versioned using your existing source control services and integrations.&lt;/p&gt;

&lt;p&gt;Here’s a sample Kubernetes Deployment configuration file for a Flask app:&lt;/p&gt;
&lt;div class="code-label " title="flask_deployment.yaml"&gt;flask_deployment.yaml&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;apiVersion: apps/v1
kind: Deployment
metadata:
  name: flask-app
  labels:
    app: flask-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: flask-app
  template:
    metadata:
      labels:
        app: flask-app
    spec:
      containers:
      - name: flask
        image: sammy/flask_app:1.0
        ports:
        - containerPort: 8080
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This Deployment launches 3 Pods that run a container called &lt;code&gt;flask&lt;/code&gt; using the &lt;code&gt;sammy/flask_app&lt;/code&gt; image (version &lt;code&gt;1.0&lt;/code&gt;) with port &lt;code&gt;8080&lt;/code&gt; open. The Deployment is called &lt;code&gt;flask-app&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To learn more about Kubernetes Pods and Deployments, consult the &lt;a href="https://kubernetes.io/docs/concepts/workloads/pods/pod/"&gt;Pods&lt;/a&gt; and &lt;a href="https://kubernetes.io/docs/concepts/workloads/controllers/deployment/"&gt;Deployments&lt;/a&gt; sections of the official Kubernetes documentation.&lt;/p&gt;

&lt;h3 id="configure-pod-storage"&gt;Configure Pod Storage&lt;/h3&gt;

&lt;p&gt;Kubernetes manages Pod storage using Volumes, Persistent Volumes (PVs) and Persistent Volume Claims (PVCs). Volumes are the Kubernetes abstraction used to manage Pod storage, and support most cloud provider block storage offerings, as well as local storage on the Nodes hosting the running Pods. To see a full list of supported Volume types, consult the Kubernetes &lt;a href="https://kubernetes.io/docs/concepts/storage/volumes/#types-of-volumes"&gt;documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For example, if your Pod contains two NGINX containers that need to share data between them (say the first, called &lt;code&gt;nginx&lt;/code&gt; serves web pages, and the second, called &lt;code&gt;nginx-sync&lt;/code&gt; fetches the pages from an external location and updates the pages served by the &lt;code&gt;nginx&lt;/code&gt; container), your Pod spec would look something like this (here we use the &lt;a href="https://kubernetes.io/docs/concepts/storage/volumes/#emptydir"&gt;&lt;code&gt;emptyDir&lt;/code&gt;&lt;/a&gt; Volume type):&lt;/p&gt;
&lt;div class="code-label " title="pod_volume.yaml"&gt;pod_volume.yaml&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - name: nginx
    image: nginx
    volumeMounts:
    - name: nginx-web
      mountPath: /usr/share/nginx/html

  - name: nginx-sync
    image: nginx-sync
    volumeMounts:
    - name: nginx-web
      mountPath: /web-data

  volumes:
  - name: nginx-web
    emptyDir: {}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We use a &lt;code&gt;volumeMount&lt;/code&gt; for each container, indicating that we'd like to mount the &lt;code&gt;nginx-web&lt;/code&gt; volume containing the web page files at &lt;code&gt;/usr/share/nginx/html&lt;/code&gt; in the &lt;code&gt;nginx&lt;/code&gt; container and at &lt;code&gt;/web-data&lt;/code&gt; in the &lt;code&gt;nginx-sync&lt;/code&gt; container. We also define a &lt;code&gt;volume&lt;/code&gt; called &lt;code&gt;nginx-web&lt;/code&gt; of type &lt;code&gt;emptyDir&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In a similar fashion, you can configure Pod storage using cloud block storage products by modifying the &lt;code&gt;volume&lt;/code&gt; type from &lt;code&gt;emptyDir&lt;/code&gt; to the relevant cloud storage volume type.&lt;/p&gt;

&lt;p&gt;The lifecycle of a Volume is tied to the lifecycle of the Pod, but &lt;em&gt;not&lt;/em&gt; to that of a container. If a container within a Pod dies, the Volume persists and the newly launched container will be able to mount the same Volume and access its data. When a Pod gets restarted or dies, so do its Volumes, although if the Volumes consist of cloud block storage, they will simply be unmounted with data still accessible by future Pods.&lt;/p&gt;

&lt;p&gt;To preserve data across Pod restarts and updates, the PersistentVolume (PV) and PersistentVolumeClaim (PVC) objects must be used.&lt;/p&gt;

&lt;p&gt;PersistentVolumes are abstractions representing pieces of persistent storage like cloud block storage volumes or NFS storage. They are created separately from PersistentVolumeClaims, which are demands for pieces of storage by developers. In their Pod configurations, developers request persistent storage using PVCs, which Kubernetes matches with available PV Volumes (if using cloud block storage, Kubernetes can dynamically create PersistentVolumes when PersistentVolumeClaims are created).&lt;/p&gt;

&lt;p&gt;If your application requires one persistent volume per replica, which is the case with many databases, you should not use Deployments but use the StatefulSet controller, which is designed for apps that require stable network identifiers, stable persistent storage, and ordering guarantees. Deployments should be used for stateless applications, and if you define a PersistentVolumeClaim for use in a Deployment configuration, that PVC will be shared by all the Deployment's replicas.&lt;/p&gt;

&lt;p&gt;To learn more about the StatefulSet controller, consult the Kubernetes &lt;a href="https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/"&gt;documentation&lt;/a&gt;. To learn more about PersistentVolumes and PersistentVolume claims, consult the Kubernetes storage &lt;a href="https://kubernetes.io/docs/concepts/storage/persistent-volumes/"&gt;documentation&lt;/a&gt;. &lt;/p&gt;

&lt;h3 id="injecting-configuration-data-with-kubernetes"&gt;Injecting Configuration Data with Kubernetes&lt;/h3&gt;

&lt;p&gt;Similar to Docker, Kubernetes provides the &lt;code&gt;env&lt;/code&gt; and &lt;code&gt;envFrom&lt;/code&gt; fields for setting environment variables in Pod configuration files. Here's a sample snippet from a Pod configuration file that sets the &lt;code&gt;HOSTNAME&lt;/code&gt; environment variable in the running Pod to &lt;code&gt;my_hostname&lt;/code&gt; :&lt;/p&gt;
&lt;div class="code-label " title="sample_pod.yaml"&gt;sample_pod.yaml&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;...
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9
        ports:
        - containerPort: 80
        env:
        - name: HOSTNAME
          value: my_hostname
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This allows you to move configuration out of Dockerfiles and into Pod and Deployment configuration files. A key advantage of further externalizing configuration from your Dockerfiles is that you can now modify these Kubernetes workload configurations (say, by changing the &lt;code&gt;HOSTNAME&lt;/code&gt; value to &lt;code&gt;my_hostname_2&lt;/code&gt;) separately from your application container definitions. Once you modify the Pod configuration file, you can then redeploy the Pod using its new environment, while the underlying container image (defined via its Dockerfile) does not need to be rebuilt, tested, and pushed to a repository. You can also version these Pod and Deployment configurations separately from your Dockerfiles, allowing you to quickly detect breaking changes and further separate config issues from application bugs.&lt;/p&gt;

&lt;p&gt;Kubernetes provides another construct for further externalizing and managing configuration data: ConfigMaps and Secrets.&lt;/p&gt;

&lt;h3 id="configmaps-and-secrets"&gt;ConfigMaps and Secrets&lt;/h3&gt;

&lt;p&gt;ConfigMaps allow you to save configuration data as objects that you then reference in your Pod and Deployment configuration files, so that you can avoid hardcoding configuration data and reuse it across Pods and Deployments. &lt;/p&gt;

&lt;p&gt;Here's an example, using the Pod config from above. We'll first save the &lt;code&gt;HOSTNAME&lt;/code&gt; environment variable as a ConfigMap, and then reference it in the Pod config:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl create configmap hostname --from-literal=HOSTNAME=my_host_name
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To reference it from the Pod configuration file, we use the the &lt;code&gt;valueFrom&lt;/code&gt; and &lt;code&gt;configMapKeyRef&lt;/code&gt; constructs:&lt;/p&gt;
&lt;div class="code-label " title="sample_pod_configmap.yaml"&gt;sample_pod_configmap.yaml&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;...
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9
        ports:
        - containerPort: 80
        env:
        - name: HOSTNAME
          valueFrom:
            configMapKeyRef:
              name: hostname
              key: HOSTNAME
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So the &lt;code&gt;HOSTNAME&lt;/code&gt; environment variable's value has been completely externalized from configuration files. We can then update these variables across all Deployments and Pods referencing them, and restart the Pods for the changes to take effect. &lt;/p&gt;

&lt;p&gt;If your applications use configuration files, ConfigMaps additionally allow you to store these files as ConfigMap objects (using the &lt;code&gt;--from-file&lt;/code&gt; flag), which you can then mount into containers as configuration files.&lt;/p&gt;

&lt;p&gt;Secrets provide the same essential functionality as ConfigMaps, but should be used for sensitive data like database credentials as the values are base64-encoded.&lt;/p&gt;

&lt;p&gt;To learn more about ConfigMaps and Secrets consult the Kubernetes &lt;a href="https://kubernetes.io/docs/concepts/configuration/"&gt;documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id="create-services"&gt;Create Services&lt;/h3&gt;

&lt;p&gt;Once you have your application up and running in Kubernetes, every Pod will be assigned an (internal) IP address, shared by its containers. If one of these Pods is removed or dies, newly started Pods will be assigned different IP addresses. &lt;/p&gt;

&lt;p&gt;For long-running services that expose functionality to internal and/or external clients, you may wish to grant a set of Pods performing the same function (or Deployment) a stable IP address that load balances requests across its containers. You can do this using a Kubernetes Service.&lt;/p&gt;

&lt;p&gt;Kubernetes Services have 4 types, specified by the &lt;code&gt;type&lt;/code&gt; field in the Service configuration file:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ClusterIP&lt;/code&gt;: This is the default type, which grants the Service a stable internal IP accessible from anywhere inside of the cluster.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;NodePort&lt;/code&gt;: This will expose your Service on each Node at a static port, between 30000-32767 by default. When a request hits a Node at its Node IP address and the &lt;code&gt;NodePort&lt;/code&gt; for your service, the request will be load balanced and routed to the application containers for your service.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;LoadBalancer&lt;/code&gt;: This will create a load balancer using your cloud provider's load balancing product, and configure a &lt;code&gt;NodePort&lt;/code&gt; and &lt;code&gt;ClusterIP&lt;/code&gt; for your Service to which external requests will be routed.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ExternalName&lt;/code&gt;: This Service type allows you to map a Kubernetes Service to a DNS record. It can be used for accessing external services from your Pods using Kubernetes DNS.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note that creating a Service of type &lt;code&gt;LoadBalancer&lt;/code&gt; for each Deployment running in your cluster will create a new cloud load balancer for each Service, which can become costly. To manage routing external requests to multiple services using a single load balancer, you can use an Ingress Controller. Ingress Controllers are beyond the scope of this article, but to learn more about them you can consult the Kubernetes &lt;a href="https://kubernetes.io/docs/concepts/services-networking/ingress/"&gt;documentation&lt;/a&gt;. A popular simple Ingress Controller is the &lt;a href="https://github.com/kubernetes/ingress-nginx"&gt;NGINX Ingress Controller&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here’s a simple Service configuration file for the Flask example used in the Pods and Deployments &lt;a href="https://www.digitalocean.com/community/tutorials/modernizing-applications-for-kubernetes#write-deployment-and-pod-configuration-files"&gt;section&lt;/a&gt; of this guide:&lt;/p&gt;
&lt;div class="code-label " title="flask_app_svc.yaml"&gt;flask_app_svc.yaml&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;apiVersion: v1
kind: Service
metadata:
  name: flask-svc
spec:
  ports:
  - port: 80
    targetPort: 8080
  selector:
    app: flask-app
  type: LoadBalancer
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we choose to expose the &lt;code&gt;flask-app&lt;/code&gt; Deployment using this &lt;code&gt;flask-svc&lt;/code&gt; Service. We create a cloud load balancer to route traffic from load balancer port &lt;code&gt;80&lt;/code&gt; to exposed container port &lt;code&gt;8080&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;To learn more about Kubernetes Services, consult the &lt;a href="https://kubernetes.io/docs/concepts/services-networking/service/"&gt;Services&lt;/a&gt; section of the Kubernetes docs.&lt;/p&gt;

&lt;h3 id="logging-and-monitoring"&gt;Logging and Monitoring&lt;/h3&gt;

&lt;p&gt;Parsing through individual container and Pod logs using &lt;code&gt;kubectl logs&lt;/code&gt; and &lt;code&gt;docker logs&lt;/code&gt; can get tedious as the number of running applications grows. To help you debug application or cluster issues, you should implement centralized logging. At a high level, this consists of agents running on all the worker nodes that process Pod log files and streams, enrich them with metadata, and forward the logs off to a backend like &lt;a href="https://github.com/elastic/elasticsearch"&gt;Elasticsearch&lt;/a&gt;. From there, log data can be visualized, filtered, and organized using a visualization tool like &lt;a href="https://github.com/elastic/kibana"&gt;Kibana&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In the container-level logging section, we discussed the recommended Kubernetes approach of having applications in containers log to the stdout/stderr streams. We also briefly discussed logging sidecar containers that can grant you more flexibility when logging from your application. You could also run logging agents directly in your Pods that capture local log data and forward them directly to your logging backend. Each approach has its pros and cons, and resource utilization tradeoffs (for example, running a logging agent container inside of each Pod can become resource-intensive and quickly overwhelm your logging backend). To learn more about different logging architectures and their tradeoffs, consult the Kubernetes &lt;a href="https://kubernetes.io/docs/concepts/cluster-administration/logging/"&gt;documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In a standard setup, each Node runs a logging agent like &lt;a href="https://www.elastic.co/products/beats/filebeat"&gt;Filebeat&lt;/a&gt; or &lt;a href="https://github.com/fluent/fluentd"&gt;Fluentd&lt;/a&gt; that picks up container logs created by Kubernetes. Recall that Kubernetes creates JSON log files for containers on the Node (in most installations these can be found at &lt;code&gt;/var/lib/docker/containers/&lt;/code&gt;). These should be rotated using a tool like logrotate. The Node logging agent should be run as a &lt;a href="https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/"&gt;DaemonSet Controller&lt;/a&gt;, a type of Kubernetes Workload that ensures that every Node runs a copy of the DaemonSet Pod. In this case the Pod would contain the logging agent and its configuration, which processes logs from files and directories mounted into the logging DaemonSet Pod.&lt;/p&gt;

&lt;p&gt;Similar to the bottleneck in using &lt;code&gt;kubectl logs&lt;/code&gt; to debug container issues, eventually you may need to consider a more robust option than simply using &lt;code&gt;kubectl top&lt;/code&gt; and the Kubernetes Dashboard to monitor Pod resource usage on your cluster. Cluster and application-level monitoring can be set up using the &lt;a href="https://prometheus.io/"&gt;Prometheus&lt;/a&gt; monitoring system and time-series database, and &lt;a href="https://github.com/grafana/grafana"&gt;Grafana&lt;/a&gt; metrics dashboard. Prometheus works using a "pull" model, which scrapes HTTP endpoints (like &lt;code&gt;/metrics/cadvisor&lt;/code&gt; on the Nodes, or the &lt;code&gt;/metrics&lt;/code&gt; application REST API endpoints) periodically for metric data, which it then processes and stores. This data can then be analyzed and visualized using Grafana dashboard. Prometheus and Grafana can be launched into a Kubernetes cluster like any other Deployment and Service.&lt;/p&gt;

&lt;p&gt;For added resiliency, you may wish to run your logging and monitoring infrastructure on a separate Kubernetes cluster, or using external logging and metrics services.&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Migrating and modernizing an application so that it can efficiently run in a Kubernetes cluster often involves non-trivial amounts of planning and architecting of software and infrastructure changes. Once implemented, these changes allow service owners to continuously deploy new versions of their apps and easily scale them as necessary, with minimal amounts of manual intervention. Steps like externalizing configuration from your app, setting up proper logging and metrics publishing, and configuring health checks allow you to fully take advantage of the Cloud Native paradigm that Kubernetes has been designed around. By building portable containers and managing them using Kubernetes objects like Deployments and Services, you can fully use your available compute infrastructure and development resources. &lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-install-run-connect-to-jupyter-notebook-on-remote-server</id>
    <published>2018-09-11T22:38:20Z</published>
    <updated>2018-09-12T19:30:54Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-install-run-connect-to-jupyter-notebook-on-remote-server"/>
    <title>How to Install, Run, and Connect to Jupyter Notebook on a Remote Server</title>
    <content type="html">&lt;p&gt;&lt;em&gt;The author selected &lt;a href="https://www.brightfunds.org/organizations/apache-software-foundation"&gt;the Apache Software Foundation&lt;/a&gt; to receive a $100 donation as part of the &lt;a href="https://do.co/w4do-cta"&gt;Write for DOnations&lt;/a&gt; program.&lt;/em&gt;&lt;/p&gt;

&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://jupyter-notebook.readthedocs.io/en/stable/"&gt;Jupyter Notebook&lt;/a&gt; is an open-source, interactive web application that allows you to write and run computer code in more than 40 programming languages, including &lt;a href="https://www.python.org/"&gt;Python&lt;/a&gt;, &lt;a href="https://www.r-project.org/"&gt;R&lt;/a&gt;, &lt;a href="https://julialang.org/"&gt;Julia&lt;/a&gt;, and &lt;a href="https://www.scala-lang.org/"&gt;Scala&lt;/a&gt;. A product from &lt;a href="http://jupyter.org/about"&gt;Project Jupyter&lt;/a&gt;, Jupyter Notebook is useful for iterative coding as it allows you to write a small snippet of code, run it, and return the result.&lt;/p&gt;

&lt;p&gt;Jupyter Notebook provides the ability to create notebook documents, referred to simply as "notebooks". Notebooks created from the Jupyter Notebook are shareable, reproducible research documents which include rich text elements, equations, code and their outputs (figures, tables, interactive plots). Notebooks can also be exported into raw code files, HTML or PDF documents, or used to create interactive slideshows or web pages.&lt;/p&gt;

&lt;p&gt;This article will walk you through how to install and configure the Jupyter Notebook application on an Ubuntu 18.04 web server and how to connect to it from your local computer. Additionally, we will also go over how to use Jupyter Notebook to run some example Python code.&lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;To complete this tutorial, you will need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One Ubuntu 18.04 server instance. This server must have a non-root user with sudo privileges and a firewall configured. Set this up by following our &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-18-04"&gt;initial server setup guide&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Python 3, pip, and the Python &lt;code&gt;venv&lt;/code&gt; module installed on the server. Do this by following Steps 1 and 2 of our tutorial on &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-python-3-and-set-up-a-programming-environment-on-an-ubuntu-18-04-server"&gt;How To Install Python 3 and Set Up a Local Programming Environment on Ubuntu 18.04&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;A modern web browser running on your local computer which you will use to access Jupyter Notebook.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Additionally, if your local computer is running Windows, you will need to install PuTTY on it in order to establish an SSH tunnel to your server. Follow our guide on &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-use-ssh-keys-with-putty-on-digitalocean-droplets-windows-users"&gt;How to Create SSH Keys with PuTTY on Windows&lt;/a&gt; to download and install PuTTY.&lt;/p&gt;

&lt;h2 id="step-1-—-installing-jupyter-notebook"&gt;Step 1 — Installing Jupyter Notebook&lt;/h2&gt;

&lt;p&gt;Since notebooks are used to write, run and see the result of small snippets of code, you will first need to set up the programming language support. Jupyter Notebook uses a language-specific &lt;em&gt;kernel&lt;/em&gt;, a computer program that runs and introspects code. Jupyter Notebook has &lt;a href="https://github.com/jupyter/jupyter/wiki/Jupyter-kernels"&gt;many kernels in different languages&lt;/a&gt;, the default being &lt;a href="https://ipython.org/"&gt;IPython&lt;/a&gt;. In this tutorial, you will set up Jupyter Notebook to run Python code through the IPython kernel.&lt;/p&gt;

&lt;p&gt;Assuming that you followed the tutorials linked in the Prerequisites section, you should have &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-python-3-and-set-up-a-local-programming-environment-on-ubuntu-18-04"&gt;Python 3, pip and a virtual environment installed&lt;/a&gt;. The examples in this guide follow the convention used in the prerequisite tutorial on installing Python 3, which names the virtual environment "&lt;code&gt;my_env&lt;/code&gt;", but you should feel free to rename it.&lt;/p&gt;

&lt;p&gt;Begin by activating the virtual environment:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;source &lt;span class="highlight"&gt;my_env&lt;/span&gt;/bin/activate
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Following this, your prompt will be prefixed with the name of your environment.&lt;/p&gt;

&lt;p&gt;Now that you're in your virtual environment, go ahead and install Jupyter Notebook:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="(my_env) sammy@server:~$"&gt;python3 -m pip install jupyter
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If the installation was successful, you will see an output similar to the following:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;. . .
Successfully installed MarkupSafe-1.0 Send2Trash-1.5.0 backcall-0.1.0 bleach-2.1.3 decorator-4.3.0 entrypoints-0.2.3 html5lib-1.0.1 ipykernel-4.8.2 ipython-6.4.0 ipython-genutils-0.2.0 ipywidgets-7.2.1 jedi-0.12.0 jinja2-2.10 jsonschema-2.6.0 jupyter-1.0.0 jupyter-client-5.2.3 jupyter-console-5.2.0 jupyter-core-4.4.0 mistune-0.8.3 nbconvert-5.3.1 nbformat-4.4.0 notebook-5.5.0 pandocfilters-1.4.2 parso-0.2.0 pexpect-4.5.0 pickleshare-0.7.4 prompt-toolkit-1.0.15 ptyprocess-0.5.2 pygments-2.2.0 python-dateutil-2.7.3 pyzmq-17.0.0 qtconsole-4.3.1 simplegeneric-0.8.1 six-1.11.0 terminado-0.8.1 testpath-0.3.1 tornado-5.0.2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With that, Jupyter Notebook has been installed onto your server. Next, we will go over how to run the application.&lt;/p&gt;

&lt;h2 id="step-2-—-running-the-jupyter-notebook"&gt;Step 2 — Running the Jupyter Notebook&lt;/h2&gt;

&lt;p&gt;Jupyter Notebook must be run from your VPS so that you can connect to it from your local machine using an SSH Tunnel and your favorite web browser.&lt;/p&gt;

&lt;p&gt;To run the Jupyter Notebook server, enter the following command:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="(my_env) sammy@server:~$"&gt;jupyter notebook
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After running this command, you will see output similar to the following:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;[I 19:46:22.031 NotebookApp] Writing notebook server cookie secret to /home/&lt;span class="highlight"&gt;sammy&lt;/span&gt;/.local/share/jupyter/runtime/notebook_cookie_secret
[I 19:46:22.365 NotebookApp] Serving notebooks from local directory: /home/&lt;span class="highlight"&gt;sammy&lt;/span&gt;/environments
[I 19:46:22.365 NotebookApp] 0 active kernels
[I 19:46:22.366 NotebookApp] The Jupyter Notebook is running at:
[I 19:46:22.366 NotebookApp] http://localhost:8888/?token=&lt;span class="highlight"&gt;Example_Jupyter_Token_3cadb8b8b7005d9a46ca4d6675&lt;/span&gt;
[I 19:46:22.366 NotebookApp] Use Control-C to stop this server and shut down all kernels (twice to skip confirmation).
[W 19:46:22.366 NotebookApp] No web browser found: could not locate runnable browser.
[C 19:46:22.367 NotebookApp]

    Copy/paste this URL into your browser when you connect for the first time,
    to login with a token:
        http://localhost:8888/?token=&lt;span class="highlight"&gt;Example_Jupyter_Token_3cadb8b8b7005d9a46ca4d6675&lt;/span&gt;&amp;amp;token&lt;span class="highlight"&gt;Example_Jupyter_Token_3cadb8b8b7005d9a46ca4d6675&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You might notice in the output that there is a &lt;code&gt;No web browser found&lt;/code&gt; warning. This is to be expected, since the application is running on a server and you likely haven't installed a web browser onto it. This guide will go over how to connect to the Notebook on the server using SSH tunneling in the next section.&lt;/p&gt;

&lt;p&gt;For now, exit the Jupyter Notebook by pressing &lt;code&gt;CTRL+C&lt;/code&gt; followed by &lt;code&gt;y&lt;/code&gt;, and then pressing &lt;code&gt;ENTER&lt;/code&gt; to confirm:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Shutdown this notebook server (y/[n])? &lt;span class="highlight"&gt;y&lt;/span&gt;
[C 20:05:47.654 NotebookApp] Shutdown confirmed
[I 20:05:47.654 NotebookApp] Shutting down 0 kernels
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then log out of the server by using the &lt;code&gt;exit&lt;/code&gt; command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;exit
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You've just run Jupyter Notebook on your server. However, in order to access the application and start working with notebooks, you'll need to connect to the application using SSH tunneling and a web browser on your local computer.&lt;/p&gt;

&lt;h2 id="step-3-—-connecting-to-the-jupyter-notebook-application-with-ssh-tunneling"&gt;Step 3 — Connecting to the Jupyter Notebook Application with SSH Tunneling&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;SSH tunneling&lt;/em&gt; is a simple and fast way to connect to the Jupyter Notebook application running on your server. Secure shell (more commonly known as &lt;a href="https://www.digitalocean.com/community/tutorials/ssh-essentials-working-with-ssh-servers-clients-and-keys"&gt;SSH&lt;/a&gt;) is a network protocol which enables you to connect to a remote server securely over an unsecured network.&lt;/p&gt;

&lt;p&gt;The SSH protocol includes a port forwarding mechanism that allows you to tunnel certain applications running on a specific port number on a server to a specific port number on your local computer. We will learn how to securely "forward" the Jupyter Notebook application running on your server (on port &lt;code&gt;8888&lt;/code&gt;, by default) to a port on your local computer.&lt;/p&gt;

&lt;p&gt;The method you use for establishing an SSH tunnel will depend on your local computer's operating system. Jump to the subsection below that is most relevant for your machine.&lt;/p&gt;

&lt;p&gt;&lt;span class='note'&gt;&lt;strong&gt;Note:&lt;/strong&gt; It's possible to set up and install the Jupyter Notebook using the DigitalOcean Web Console, but connecting to the application via an SSH tunnel must be done through the terminal or with PuTTY.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;

&lt;h3 id="ssh-tunneling-using-macos-or-linux"&gt;SSH Tunneling using macOS or Linux&lt;/h3&gt;

&lt;p&gt;If your local computer is running Linux or macOS, it's possible to establish an SSH tunnel just by running a single command.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ssh&lt;/code&gt; is the standard command to open an SSH connection, but when used with the &lt;code&gt;-L&lt;/code&gt; directive, you can specify that a given port on the local host (that is, your local machine) will be forwarded to a given host and port on the remote host (in this case, your server). This means that whatever is running on the specified port on the remote server (&lt;code&gt;8888&lt;/code&gt;, Jupyter Notebook's default port) will appear on the specified port on your local computer (&lt;code&gt;&lt;span class="highlight"&gt;8000&lt;/span&gt;&lt;/code&gt; in the example command).&lt;/p&gt;

&lt;p&gt;To establish your own SSH tunnel, run the following command. Feel free to change port &lt;code&gt;&lt;span class="highlight"&gt;8000&lt;/span&gt;&lt;/code&gt; to one of your choosing if, for example, &lt;code&gt;8000&lt;/code&gt; is in use by another process. It is recommended that you use a port greater than or equal to &lt;code&gt;8000&lt;/code&gt;, as those port numbers are unlikely to be used by another process. Be sure to include your own server's IP address and the name of your server's non-root user:&lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;ssh -L &lt;span class="highlight"&gt;8000&lt;/span&gt;:localhost:8888 &lt;span class="highlight"&gt;sammy&lt;/span&gt;@&lt;span class="highlight"&gt;your_server_ip&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If there are no errors from this command, it will log you into your remote server. From there, activate the virtual environment:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;source ~/environments/&lt;span class="highlight"&gt;my_env&lt;/span&gt;/bin/activate
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then run the Jupyter Notebook application:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="(my_env) sammy@server:~$"&gt;jupyter notebook
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To connect to Jupyter Notebook, use your favorite web browser to navigate to the local port on the local host: &lt;code&gt;http://localhost:&lt;span class="highlight"&gt;8000&lt;/span&gt;&lt;/code&gt;. Now that you're connected to Jupyter Notebook, continue on to Step 4 to learn how to use it.&lt;/p&gt;

&lt;h3 id="ssh-tunneling-using-windows-and-putty"&gt;SSH Tunneling using Windows and PuTTY&lt;/h3&gt;

&lt;p&gt;PuTTY is an open-source SSH client for Windows which can be used to &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-use-ssh-keys-with-putty-on-digitalocean-droplets-windows-users"&gt;connect to your server&lt;/a&gt;. After downloading and installing PuTTY on your Windows machine (as described in the prerequisite tutorial), open the program and enter your server URL or IP address, as shown here:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/jupyter_notebook/JN_putty_1.png" alt="Enter server URL or IP into Putty"&gt;&lt;/p&gt;

&lt;p&gt;Next, click &lt;strong&gt;+ SSH&lt;/strong&gt; at the bottom of the left pane, and then click &lt;strong&gt;Tunnels&lt;/strong&gt;. In this window, enter the port that you want to use to access Jupyter on your local machine (&lt;code&gt;&lt;span class="highlight"&gt;8000&lt;/span&gt;&lt;/code&gt; ). It is recommended to use a port greater or equal to &lt;code&gt;8000&lt;/code&gt; as those port numbers are unlikely to be used by another process. If &lt;code&gt;8000&lt;/code&gt; is used by another process, though, select a different, unused port number. Next, set the destination as &lt;code&gt;localhost:8888&lt;/code&gt;, since port &lt;code&gt;8888&lt;/code&gt; is the one that Jupyter Notebook is running on. Then click the &lt;strong&gt;Add&lt;/strong&gt; button and the ports should appear in the &lt;strong&gt;Forwarded ports&lt;/strong&gt; field:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/jupyter_notebook/JN_putty_2.png" alt="Configure SSH tunnel in Putty"&gt;&lt;/p&gt;

&lt;p&gt;Finally, click the &lt;strong&gt;Open&lt;/strong&gt; button. This will both connect your machine to the server via SSH and tunnel the desired ports. If no errors show up, go ahead and activate your virtual environment:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;source ~/environments/&lt;span class="highlight"&gt;my_env&lt;/span&gt;/bin/activate
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then run Jupyter Notebook:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="(my_env) sammy@server:~$"&gt;jupyter notebook
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, navigate to the local port in your favorite web browser, for example &lt;code&gt;http://localhost:&lt;span class="highlight"&gt;8000&lt;/span&gt;&lt;/code&gt; (or whatever port number you chose), to connect to the Jupyter Notebook instance running on the server. Now that you're connected to Jupyter Notebook, continue on to Step 4 to learn how to use it.&lt;/p&gt;

&lt;h2 id="step-4-—-using-jupyter-notebook"&gt;Step 4 — Using Jupyter Notebook&lt;/h2&gt;

&lt;p&gt;When accessed through a web browser, Jupyter Notebook provides a Notebook Dashboard which acts as a file browser and gives you an interface for creating, editing and exploring notebooks. Think of these notebooks as documents (saved with a &lt;code&gt;.ipynb&lt;/code&gt; file extension) which you populate with any number of individual cells. Each cell holds an interactive text editor which can be used to run code or write rendered text. Additionally, notebooks allow you to write and run equations, include other rich media, such as images or interactive plots, and they can be exported and shared in various formats (&lt;code&gt;.ipyb&lt;/code&gt;, &lt;code&gt;.pdf&lt;/code&gt;, &lt;code&gt;.py&lt;/code&gt;). To illustrate some of these functions, we'll create a notebook file from the Notebook Dashboard, write a simple text board with an equation, and run some basic Python 3 code.&lt;/p&gt;

&lt;p&gt;By this point you should have connected to the server using an SSH tunnel and started the Jupyter Notebook application from your server. After navigating to &lt;code&gt;http://localhost:&lt;span class="highlight"&gt;8000&lt;/span&gt;&lt;/code&gt;, you will be presented with a login page:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/jupyter_notebook/JN_login_screen_small.png" alt="Jupyter Notebook login screen"&gt;&lt;/p&gt;

&lt;p&gt;In the &lt;strong&gt;Password or token&lt;/strong&gt; field at the top, enter the token shown in the output after you ran &lt;code&gt;jupyter notebook&lt;/code&gt; from your server:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;[I 20:35:17.004 NotebookApp] Writing notebook server cookie secret to /run/user/1000/jupyter/notebook_cookie_secret
[I 20:35:17.314 NotebookApp] Serving notebooks from local directory: /home/sammy
[I 20:35:17.314 NotebookApp] 0 active kernels
[I 20:35:17.315 NotebookApp] The Jupyter Notebook is running at:
[I 20:35:17.315 NotebookApp] http://localhost:8888/?token=&lt;span class="highlight"&gt;Example_Jupyter_Token_3cadb8b8b7005d9a46ca4d6675&lt;/span&gt;
[I 20:35:17.315 NotebookApp] Use Control-C to stop this server and shut down all kernels (twice to skip confirmation).
[W 20:35:17.315 NotebookApp] No web browser found: could not locate runnable browser.
[C 20:35:17.316 NotebookApp]
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Alternatively, you can copy that URL from your terminal output and paste it into your browser's address bar.&lt;/p&gt;

&lt;p&gt;Automatically, Jupyter notebook will show all of the files and folders stored in the directory from which it's run. Create a new notebook file by clicking &lt;strong&gt;New&lt;/strong&gt; then &lt;strong&gt;Python 3&lt;/strong&gt; at the top-right of the Notebook Dashboard:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/jupyter_notebook/JN_new_python3.png" alt="Create a new Python3 notebook"&gt;&lt;/p&gt;

&lt;p&gt;Within this new notebook, change the first cell to accept markdown syntax by clicking &lt;strong&gt;Cell&lt;/strong&gt; &amp;gt; &lt;strong&gt;Cell Type&lt;/strong&gt; &amp;gt; &lt;strong&gt;Markdown&lt;/strong&gt; on the navigation bar at the top. In addition to markdown, this Cell Type also allows you to write equations in LaTeX. For example, type the following into the cell after changing it to markdown:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;# Simple Equation

Let us now implement the following equation in Python:
$$ y = x^2$$

where $x = 2$
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To turn the markdown into rich text, press &lt;code&gt;CTRL + ENTER&lt;/code&gt; and the following should be the result:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/jupyter_notebook/JN_sample_equation.png" alt="Turn sample equation into rich text"&gt;&lt;/p&gt;

&lt;p&gt;You can use the markdown cells to make notes and document your code.&lt;/p&gt;

&lt;p&gt;Now, let's implement a simple equation and print the result. Click &lt;strong&gt;Insert&lt;/strong&gt; &amp;gt; &lt;strong&gt;Insert Cell Below&lt;/strong&gt; to insert a cell. In this new cell, enter the following code:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;x = 2
y = x*x
print(y)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To run the code, press &lt;code&gt;CTRL + ENTER&lt;/code&gt;, and the following will be the result:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/jupyter_notebook/JN_sample_equation2.png" alt="Solve sample equation"&gt;&lt;/p&gt;

&lt;p&gt;These are some relatively simple examples of what you can do with Jupyter Notebook. However, it is a very powerful application with many potential use cases. From here, you can add some Python libraries and use the notebook as you would with any other Python development environment.&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;You should be now able to write reproducible Python code and text using the Jupyter Notebook running on a remote server. To get a quick tour of Jupyter Notebook, click &lt;strong&gt;Help&lt;/strong&gt; in the top navigation bar and select &lt;strong&gt;User Interface Tour&lt;/strong&gt; as shown here:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/jupyter_notebook/JN_help_tour.png" alt="Finding Jupyter Notebook help tour"&gt;&lt;/p&gt;

&lt;p&gt;If you're interested, we encourage you to learn more about Jupyter Notebook by going through the &lt;a href="http://jupyter.org/documentation"&gt;Project Jupyter documentation&lt;/a&gt;. Additionally, you can build on what you learned in this tutorial by &lt;a href="https://www.digitalocean.com/community/tutorial_series/how-to-code-in-python-3"&gt;learning how to code in Python 3&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-install-r-on-debian-9</id>
    <published>2018-09-11T17:52:58Z</published>
    <updated>2018-09-11T18:03:18Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-install-r-on-debian-9"/>
    <title>How To Install R on Debian 9</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;R is an open-source programming language that specializes in statistical computing and graphics. Supported by the R Foundation for Statistical Computing, it is widely used for developing statistical software and performing data analysis. An increasingly popular and extensible language with an active community, R offers many user-generated packages for specific areas of study, which makes it applicable to many fields.&lt;/p&gt;

&lt;p&gt;In this tutorial, we will install R and show how to add packages from the official &lt;a href="https://cloud.r-project.org/"&gt;Comprehensive R Archive Network (CRAN)&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;To follow along with this tutorial, you will need a Debian 9 server with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;at least&lt;/em&gt; 1GB of RAM&lt;/li&gt;
&lt;li&gt;a non-root user with &lt;code&gt;sudo&lt;/code&gt; privileges &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To learn how to achieve this setup, follow our &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-debian-9"&gt;Debian 9 initial server setup guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Once these prerequisites are in place, you’re ready to begin.&lt;/p&gt;

&lt;h2 id="step-1-—-installing-dependencies"&gt;Step 1 — Installing Dependencies&lt;/h2&gt;

&lt;p&gt;Because R is a fast-moving project, the latest stable version isn’t always available from Debian’s repositories, so we’ll need to add the external repository maintained by CRAN. In order to do this, we’ll need to install some dependencies for the Debian 9 cloud image.&lt;/p&gt;

&lt;p&gt;To perform network operations that manage and download certificates, we need to install &lt;code&gt;dirmngr&lt;/code&gt; so that we can add the external repository. &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install dirmngr --install-recommends
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To add a PPA reference to Debian, we’ll need to use the &lt;code&gt;add-apt-repository&lt;/code&gt; command. For installations where this command may not available, you can add this utility to your system by installing &lt;code&gt;software-properties-common&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install software-properties-common
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, to ensure that we have HTTPS support for secure protocols, we’ll install the following tool:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install apt-transport-https
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With these dependencies in place, we’re ready to install R.&lt;/p&gt;

&lt;h2 id="step-2-—-installing-r"&gt;Step 2 —  Installing R&lt;/h2&gt;

&lt;p&gt;For the most recent version of R, we’ll be installing from the CRAN repositories. &lt;/p&gt;

&lt;p&gt;&lt;span class='note'&gt;&lt;strong&gt;Note:&lt;/strong&gt; CRAN maintains the repositories within their network, but not all external repositories are reliable. Be sure to install only from trusted sources.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;Let’s first add the relevant GPG key.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt-key adv --keyserver keys.gnupg.net --recv-key 'E19F5F87128899B192B1A2C2AD5F960A256A04AF'
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When we run the command, we’ll receive the following output:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Executing: /tmp/apt-key-gpghome.k3UoM7WQGq/gpg.1.sh --keyserver keys.gnupg.net --recv-key E19F5F87128899B192B1A2C2AD5F960A256A04AF
gpg: key AD5F960A256A04AF: public key "Johannes Ranke (Wissenschaftlicher Berater) &amp;lt;johannes.ranke@jrwb.de&amp;gt;" imported
gpg: Total number processed: 1
gpg:               imported: 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once we have the trusted key, we can add the repository. Note that if you’re not using Debian 9 (Stretch), you can look at the &lt;a href="https://cran.r-project.org/bin/linux/debian/#supported-branches"&gt;supported R Project Debian branches&lt;/a&gt;, named for each release. &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo add-apt-repository 'deb https://cloud.r-project.org/bin/linux/debian stretch-cran35/'
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, we’ll need to run &lt;code&gt;update&lt;/code&gt; after this in order to include package manifests from the new repository.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt update
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Among the output should be a line similar to the following:&lt;/p&gt;

&lt;p&gt;Among the output that displays, you should identify lines similar to the following:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;...
Get:6 https://cloud.r-project.org/bin/linux/debian stretch-cran35/ InRelease [4,371 B]
Get:7 https://cloud.r-project.org/bin/linux/debian stretch-cran35/ Packages [50.1 kB]
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If the lines above appear in the output from the &lt;code&gt;update&lt;/code&gt; command, we’ve successfully added the repository. We can be sure we won’t accidentally install an older version.&lt;/p&gt;

&lt;p&gt;At this point, we’re ready to install R with the following command.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install r-base
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If prompted to confirm installation, press &lt;code&gt;y&lt;/code&gt; to continue.&lt;/p&gt;

&lt;p&gt;As of the time of writing, the latest stable version of R from CRAN is 3.5.1, which is displayed when you start R. &lt;/p&gt;

&lt;p&gt;Since we’re planning to install an example package for every user on the system, we’ll start R as root so that the libraries will be available to all users automatically. Alternatively, if you run the &lt;code&gt;R&lt;/code&gt; command without &lt;code&gt;sudo&lt;/code&gt;, a personal library can be set up for your user.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo -i R
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;
R version 3.5.1 (2018-07-02) -- "Feather Spray"
Copyright (C) 2018 The R Foundation for Statistical Computing
Platform: x86_64-pc-linux-gnu (64-bit)
...
Type 'demo()' for some demos, 'help()' for on-line help, or
'help.start()' for an HTML browser interface to help.
Type 'q()' to quit R.

&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This confirms that we’ve successfully installed R and entered its interactive shell. &lt;/p&gt;

&lt;h2 id="step-3-—-installing-r-packages-from-cran"&gt;Step 3 — Installing R Packages from CRAN&lt;/h2&gt;

&lt;p&gt;Part of R’s strength is its available abundance of add-on packages. For demonstration purposes, we’ll install &lt;a href="https://cran.r-project.org/web/packages/txtplot/index.html"&gt;&lt;code&gt;txtplot&lt;/code&gt;&lt;/a&gt;, a library that outputs ASCII graphs that include scatterplot, line plot, density plot, acf and bar charts:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="&amp;gt;"&gt;install.packages('txtplot')
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;span class='note'&gt;&lt;p&gt;
&lt;strong&gt;Note:&lt;/strong&gt; The following output shows where the package will be installed.&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;...
Installing package into ‘/usr/local/lib/R/site-library’
(as ‘lib’ is unspecified)
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This site-wide path is available because we ran R as root. This is the correct location to make the package available to all users.&lt;br&gt;&lt;/p&gt;&lt;/span&gt;

&lt;p&gt;When the installation is complete, we can load &lt;code&gt;txtplot&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="&amp;gt;"&gt;library('txtplot')
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If there are no error messages, the library has successfully loaded. Let’s put it in action now with an example which demonstrates a basic plotting function with axis labels. The example data, supplied by R's &lt;code&gt;datasets&lt;/code&gt; package, contains &lt;a href="https://stat.ethz.ch/R-manual/R-devel/library/datasets/html/cars.html"&gt;the speed of cars and the distance required to stop based on data from the 1920s&lt;/a&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;txtplot(cars[,1], cars[,2], xlab = 'speed', ylab = 'distance')
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;      +----+-----------+------------+-----------+-----------+--+
  120 +                                                   *    +
      |                                                        |
d 100 +                                                   *    +
i     |                                    *                *  |
s  80 +                          *         *                   +
t     |                                       * *    *    *    |
a  60 +                          *  *      *    *      *       +
n     |                        *         * *  * *              |
c  40 +                *       * *    *  *    * *              +
e     |         *      *  * *  * *  *                          |
   20 +           *    *  * *       *                          +
      |  *      *    *                                         |
    0 +----+-----------+------------+-----------+-----------+--+
           5          10           15          20          25   
                                speed       
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you are interested to learn more about &lt;code&gt;txtplot&lt;/code&gt;,  use &lt;code&gt;help(txtplot)&lt;/code&gt; from within the R interpreter.&lt;/p&gt;

&lt;p&gt;Any precompiled package can be installed from CRAN with &lt;code&gt;install.packages()&lt;/code&gt;. To learn more about what’s available, you can find a listing of official packages organized by name via the &lt;a href="https://cran.r-project.org/web/packages/available_packages_by_name.html"&gt;Available CRAN Packages By Name list&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To exit R, you can type &lt;code&gt;q()&lt;/code&gt;. Unless you want to save the workspace image, you can press &lt;code&gt;n&lt;/code&gt;. &lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;With R successfully installed on your server, you may be interested in this guide on &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-set-up-rstudio-on-an-ubuntu-cloud-server"&gt;installing the RStudio Server&lt;/a&gt; to bring an IDE to the server-based deployment you just completed. You can also learn how to set up a &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-set-up-shiny-server-on-ubuntu-16-04"&gt;Shiny server&lt;/a&gt; to convert your R code into interactive web pages. &lt;/p&gt;

&lt;p&gt;For more information on how to install R packages by leveraging different tools, you can read about how to &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-r-packages-using-devtools"&gt;install directly from GitHub, BitBucket or other locations&lt;/a&gt;. This will allow you to take advantage of the very latest work from the active community.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/como-criar-um-cluster-kubernetes-1-10-usando-kubeadm-no-centos-7-pt</id>
    <published>2018-09-11T00:53:39Z</published>
    <updated>2018-09-11T00:57:39Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/como-criar-um-cluster-kubernetes-1-10-usando-kubeadm-no-centos-7-pt"/>
    <title>Como Criar um Cluster Kubernetes 1.10 Usando Kubeadm no CentOS 7</title>
    <content type="html">&lt;p&gt;&lt;em&gt;O autor escolheu o &lt;a href="https://www.brightfunds.org/funds/foss-nonprofits"&gt;Free and Open Source Fund&lt;/a&gt; para receber uma doação como parte do programa &lt;a href="https://do.co/w4do-cta"&gt;Write for DOnations&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h3 id="introdução"&gt;Introdução&lt;/h3&gt;

&lt;p&gt;O &lt;a href="https://kubernetes.io/"&gt;Kubernetes&lt;/a&gt; é um sistema de orquestração de container em escala. Inicialmente desenvolvido pelo Google baseado em suas experiências executando containers em produção. O Kubernetes é open source e desenvolvido ativamente por uma comunidade em todo o mundo.&lt;/p&gt;

&lt;p&gt;O &lt;a href="https://kubernetes.io/docs/reference/setup-tools/kubeadm/kubeadm/"&gt;Kubeadm&lt;/a&gt; atomatiza a instalação e a configuração de componentes do Kubernetes tais como o servidor de API, o Controller Manager, e o Kube DNS. Contudo, ele não cria usuários ou lida com a instalação de dependências no nível do sistema operacional e sua configuração. Para essa tarefas preliminares, é possível utilizar uma ferramenta de gerência de configuração como o &lt;a href="https://www.ansible.com/"&gt;Ansible&lt;/a&gt; ou o &lt;a href="https://saltstack.com/"&gt;SaltStack&lt;/a&gt;. A utilização dessas ferramentas torna a criação de clusters adicionais ou a recriação de clusters existentes muito mais simples e menos propensa a erros.&lt;/p&gt;

&lt;p&gt;Neste guia, você vai configurar um cluster Kubernetes a partir do zero utilizando o Ansible e o Kubeadm, e a seguir fazer o deploy de uma aplicação Nginx containerizada nele.&lt;/p&gt;

&lt;h2 id="objetivos"&gt;Objetivos&lt;/h2&gt;

&lt;p&gt;Seu cluster irá incluir os seguintes recursos físicos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Um node master&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;O node master (um &lt;em&gt;node&lt;/em&gt; no Kubernetes refere-se a um servidor) é responsável por gerenciar o estado do cluster. Ele roda o  &lt;a href="https://github.com/coreos/etcd"&gt;Etcd&lt;/a&gt;, que armazena dados de cluster entre componentes que fazem o scheduling de cargas de trabalho para nodes de trabalho. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Dois nodes worker&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nodes worker são os servidores onde suas &lt;em&gt;cargas de trabalho&lt;/em&gt; (i.e. aplicações e serviços containerizados) irão executar. Um worker continuará a executar sua carga de trabalho uma vez que estejam atribuídos a ela, mesmo se o master for desativado quando o scheduling estiver concluído. A capacidade de um cluster pode ser aumentada adicionando workers.&lt;/p&gt;

&lt;p&gt;Após a conclusão desse guia, você terá um cluster pronto para executar aplicações containerizadas, desde que os servidores no cluster tenham recursos suficientes de CPU e RAM para suas aplicações consumirem. Quase todas as aplicações Unix tradicionais, incluindo aplicações web, bancos de dados, daemons, e ferramentas de linha de comando podem ser containerizadas e feitas para rodar no cluster. O cluster em si consumirá cerca de 300-500MB de memória e 10% de CPU em cada node.&lt;/p&gt;

&lt;p&gt;Uma vez que o cluster esteja configurado, você fará o deploy do servidor web &lt;a href="https://nginx.org/en/"&gt;Nginx&lt;/a&gt; nele para assegurar que ele está executando as cargas de trabalho corretamente.&lt;/p&gt;

&lt;h2 id="pré-requisitos"&gt;Pré-requisitos&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Um par de chaves SSH em sua máquina Linux/macOS/BSD local. Se você não tiver usado chaves SSH antes, você pode aprender como configurá-las seguindo &lt;a href="https://www.digitalocean.com/community/tutorials/ssh-essentials-working-with-ssh-servers-clients-and-keys#generating-and-working-with-ssh-keys"&gt;esta explicação em como configurar chaves SSH em sua máquina local&lt;/a&gt;. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Três servidores rodando CentOS 7 com pelo menos 1GB de RAM. Você deve ser capaz de fazer SSH em cada servidor como usuário root com o seu par de chaves SSH. Certifique-se também de adicionar sua chave pública à conta do usuário do &lt;strong&gt;centos&lt;/strong&gt; no node master. Se você precisar de orientação sobre como adicionar uma chave SSH a uma conta de usuário específica, consulte este tutorial sobre &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-set-up-ssh-keys-on-centos7"&gt;Como Configurar Chaves SSH no CentOS7&lt;/a&gt;. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Ansible instalado em sua máquina local. Para instruções de instalação, siga a &lt;a href="http://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html#installing-the-control-machine"&gt;documentação oficial da instalação do Ansible&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Familiaridade com o playbooks do Ansible. Para uma revisão, verifique &lt;a href="https://www.digitalocean.com/community/tutorials/configuration-management-101-writing-ansible-playbooks"&gt;Configuration Management 101: Writing Ansible Playbooks&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Conhecimento de como lançar um container a partir de uma imagem Docker. Dê uma olhada no "Passo 5 — Executando um Container Docker" em &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-and-use-docker-on-centos-7#step-5-%E2%80%94-running-a-docker-container"&gt;Como instalar e usar o Docker no CentOS 7&lt;/a&gt; se precisar relembrar.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="passo-1-—-configurando-o-diretório-da-Área-de-trabalho-e-o-arquivo-de-inventário-ansible"&gt;Passo 1 — Configurando o Diretório da Área de Trabalho e o Arquivo de Inventário Ansible&lt;/h2&gt;

&lt;p&gt;Nessa seção, você vai criar um diretório em sua máquina local que irá servir como sua área de trabalho. Você configurará o Ansible localmente para que ele possa se comunicar e executar comandos em seus servidores remotos. Depois disso pronto, você irá criar um arquivo &lt;code&gt;hosts&lt;/code&gt; contendo informações de inventário tais como os endereços IP de seus servidores e os grupos aos quais cada servidor pertence. &lt;/p&gt;

&lt;p&gt;Dos seus três servidores, um será o master com um IP exibido como &lt;code&gt;&lt;span class="highlight"&gt;master_ip&lt;/span&gt;&lt;/code&gt;. Os outros dois servidores serão workers e terão os IPs &lt;code&gt;&lt;span class="highlight"&gt;worker_1_ip&lt;/span&gt;&lt;/code&gt; e &lt;code&gt;&lt;span class="highlight"&gt;worker_2_ip&lt;/span&gt;&lt;/code&gt;.  &lt;/p&gt;

&lt;p&gt;Crie um diretório chamado &lt;code&gt;~/kube-cluster&lt;/code&gt; no diretório home de sua máquina local e faça um &lt;code&gt;cd&lt;/code&gt; para dentro dele:&lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;mkdir ~/kube-cluster
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;cd ~/kube-cluster
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Esse diretório será sua área de trabalho para o restante desse tutorial e conterá todos os seus playbooks de Ansible. Ele também será o diretório no qual você irá executar todos os comandos locais.&lt;/p&gt;

&lt;p&gt;Crie um arquivo chamado &lt;code&gt;~/kube-cluster/hosts&lt;/code&gt; usando o &lt;code&gt;vi&lt;/code&gt; ou o seu editor de textos favorito: &lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;vi ~/kube-cluster/hosts
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pressione &lt;code&gt;i&lt;/code&gt; para inserir o seguinte texto ao arquivo, que irá especificar informações sobre a estrutura lógica do cluster:&lt;/p&gt;
&lt;div class="code-label " title="~/kube-cluster/hosts"&gt;~/kube-cluster/hosts&lt;/div&gt;&lt;pre class="code-pre  local-environment"&gt;&lt;code langs=""&gt;
[masters]
master ansible_host=&lt;span class="highlight"&gt;master_ip&lt;/span&gt; ansible_user=root

[workers]
worker1 ansible_host=&lt;span class="highlight"&gt;worker_1_ip&lt;/span&gt; ansible_user=root
worker2 ansible_host=&lt;span class="highlight"&gt;worker_2_ip&lt;/span&gt; ansible_user=root
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Quando tiver terminado, pressione &lt;code&gt;ESC&lt;/code&gt; seguido de &lt;code&gt;:wq&lt;/code&gt; para gravar as alterações no arquvo e sair.&lt;/p&gt;

&lt;p&gt;Você deve se lembrar de que &lt;a href="http://docs.ansible.com/ansible/latest/user_guide/intro_inventory.html"&gt;&lt;em&gt;arquivos de inventário&lt;/em&gt;&lt;/a&gt; no Ansible são utilizados para especificar informações de servidor tais como endereços IP, usuários remotos, e agrupamentos de servidores para tratar como uma unidade única para a execução de comandos. O &lt;code&gt;~/kube-cluster/hosts&lt;/code&gt; será o seu arquivo de inventário e você adicionou dois grupos Ansible a ele (&lt;strong&gt;masters&lt;/strong&gt; e &lt;strong&gt;workers&lt;/strong&gt;) especificando a estrutura lógica do seu cluster.&lt;/p&gt;

&lt;p&gt;No grupo &lt;strong&gt;masters&lt;/strong&gt;, existe uma entrada de servidor chamada "master" que lista o IP do node master (&lt;code&gt;&lt;span class="highlight"&gt;master_ip&lt;/span&gt;&lt;/code&gt;) e especifica que o Ansible deve executar comandos remotos como root.&lt;/p&gt;

&lt;p&gt;De maneira similar, no grupo &lt;strong&gt;workers&lt;/strong&gt;, existem duas entradas para os servidores workers (&lt;code&gt;&lt;span class="highlight"&gt;worker_1_ip&lt;/span&gt;&lt;/code&gt; e &lt;code&gt;&lt;span class="highlight"&gt;worker_2_ip&lt;/span&gt;&lt;/code&gt;) que também especificam o &lt;code&gt;ansible_user&lt;/code&gt; como root.&lt;/p&gt;

&lt;p&gt;Tendo configurado o inventário do servidor com grupos, vamos passar a instalar dependências no nível do sistema operacional e a criar definições de configuração.&lt;/p&gt;

&lt;h2 id="passo-2-—-criando-um-usuário-não-root-em-todos-os-servidores-remotos"&gt;Passo 2 — Criando um Usuário Não-Root em Todos os Servidores Remotos&lt;/h2&gt;

&lt;p&gt;Nesta seção você irá criar um usuário não-root com privilégios sudo em todos os servidores para que você possa fazer SSH manualmente neles como um usuário sem privilégios. Isso pode ser útil se, por exemplo, você gostaria de ver informações do sistema com comandos como &lt;code&gt;top/htop&lt;/code&gt;, ver a lista de containers em execução, ou alterar arquivos de configuração de propriedade do root. Estas operações são rotineiramente executadas durante a manutenção de um cluster, e a utilização de um usuário que não seja root para tarefas desse tipo minimiza o risco de modificação ou exclusão de arquivos importantes ou a realização não intencional de operações perigosas.&lt;/p&gt;

&lt;p&gt;Crie um arquivo chamado &lt;code&gt;~/kube-cluster/initial.yml&lt;/code&gt; na área de trabalho:&lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;vi ~/kube-cluster/initial.yml
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A seguir, adicione o seguinte play ao arquivo para criar um usuário não-root com privilégios sudo em todos os servidores. Um play no Ansible é uma coleção de passos a serem realizados que visam servidores e grupos específicos. O seguinte play irá criar um usuário sudo não-root:&lt;/p&gt;
&lt;div class="code-label " title="~/kube-cluster/initial.yml"&gt;~/kube-cluster/initial.yml&lt;/div&gt;&lt;pre class="code-pre  local-environment"&gt;&lt;code langs=""&gt;
- hosts: all
  become: yes
  tasks:
    - name: create the 'centos' user
      user: name=centos append=yes state=present createhome=yes shell=/bin/bash

    - name: allow 'centos' to have passwordless sudo
      lineinfile:
        dest: /etc/sudoers
        line: 'centos ALL=(ALL) NOPASSWD: ALL'
        validate: 'visudo -cf %s'

    - name: set up authorized keys for the centos user
      authorized_key: user=centos key="{{item}}"
      with_file:
        - ~/.ssh/id_rsa.pub
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Aqui está um detalhamento do que este playbook faz:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Cria um usuário não-root &lt;code&gt;centos&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Configura o arquivo &lt;code&gt;sudoers&lt;/code&gt; para permitir o usuário &lt;code&gt;centos&lt;/code&gt; executar comandos &lt;code&gt;sudo&lt;/code&gt; sem uma solicitação de senha.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Adiciona a chave pública em sua máquina local (normalmente &lt;code&gt;~/.ssh/id_rsa.pub&lt;/code&gt;) para a lista de chaves autorizadas do usuário remoto &lt;code&gt;centos&lt;/code&gt;. Isto o permitirá fazer SSH para dentro de cada servidor como usuário &lt;code&gt;centos&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Salve e feche o arquivo depois que tiver adicionado o texto.&lt;/p&gt;

&lt;p&gt;Em seguida, rode o playbook localmente executando:&lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;ansible-playbook -i hosts ~/kube-cluster/initial.yml
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;O comando será concluído dentro de dois a cinco minutos. Na conclusão, você verá uma saída semelhante à seguinte:&lt;/p&gt;
&lt;pre class="code-pre  local-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;PLAY [all] ****

TASK [Gathering Facts] ****
ok: [master]
ok: [worker1]
ok: [worker2]

TASK [create the 'centos' user] ****
changed: [master]
changed: [worker1]
changed: [worker2]

TASK [allow 'centos' user to have passwordless sudo] ****
changed: [master]
changed: [worker1]
changed: [worker2]

TASK [set up authorized keys for the centos user] ****
changed: [worker1] =&amp;gt; (item=ssh-rsa AAAAB3...)
changed: [worker2] =&amp;gt; (item=ssh-rsa AAAAB3...)
changed: [master] =&amp;gt; (item=ssh-rsa AAAAB3...)

PLAY RECAP ****
master                     : ok=5    changed=4    unreachable=0    failed=0   
worker1                    : ok=5    changed=4    unreachable=0    failed=0   
worker2                    : ok=5    changed=4    unreachable=0    failed=0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Agora que a configuração preliminar está completa, você pode passar para a instalação de dependências específicas do Kubernetes. &lt;/p&gt;

&lt;h2 id="passo-3-—-instalando-as-dependências-do-kubernetes"&gt;Passo 3 — Instalando as Dependências do Kubernetes&lt;/h2&gt;

&lt;p&gt;Nesta seção, você irá instalar os pacotes no nível do sistema operacional necessários pelo Kubernetes com o gerenciador de pacotes &lt;code&gt;yum&lt;/code&gt; do CentOS. Esses pacotes são:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Docker - um runtime de container. Este é o componente que executa seus containers. Suporte a outros runtimes como o &lt;a href="https://coreos.com/rkt/"&gt;rkt&lt;/a&gt; está em desenvolvimento ativo no Kubernetes. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;kubeadm&lt;/code&gt; - uma ferramenta CLI que irá instalar e configurar os vários componentes de um cluster de uma maneira padrão.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;kubelet&lt;/code&gt; - um serviço/programa de sistema que roda em todos os nodes e lida com operações no nível do node.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;kubectl&lt;/code&gt; - uma ferramenta CLI usada para emitir comandos para o cluster através de seu servidor de API.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Crie um arquivo chamado &lt;code&gt;~/kube-cluster/kube-dependencies.yml&lt;/code&gt; na área de trabalho:&lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;vi ~/kube-cluster/kube-dependencies.yml
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Adicione os seguintes plays ao arquivo para instalar esses pacotes em seus servidores:&lt;/p&gt;
&lt;div class="code-label " title="~/kube-cluster/kube-dependencies.yml"&gt;~/kube-cluster/kube-dependencies.yml&lt;/div&gt;&lt;pre class="code-pre  local-environment"&gt;&lt;code langs=""&gt;
- hosts: all
  become: yes
  tasks:
   - name: install Docker
     yum:
       name: docker
       state: present
       update_cache: true

   - name: start Docker
     service:
       name: docker
       state: started

   - name: disable SELinux
     command: setenforce 0

   - name: disable SELinux on reboot
     selinux:
       state: disabled

   - name: ensure net.bridge.bridge-nf-call-ip6tables is set to 1
     sysctl:
      name: net.bridge.bridge-nf-call-ip6tables
      value: 1
      state: present

   - name: ensure net.bridge.bridge-nf-call-iptables is set to 1
     sysctl:
      name: net.bridge.bridge-nf-call-iptables
      value: 1
      state: present

   - name: add Kubernetes' YUM repository
     yum_repository:
      name: Kubernetes
      description: Kubernetes YUM repository
      baseurl: https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64
      gpgkey: https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
      gpgcheck: yes

   - name: install kubelet
     yum:
        name: kubelet
        state: present
        update_cache: true

   - name: install kubeadm
     yum:
        name: kubeadm
        state: present

   - name: start kubelet
     service:
       name: kubelet
       enabled: yes
       state: started

- hosts: master
  become: yes
  tasks:
   - name: install kubectl
     yum:
        name: kubectl
        state: present
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;O primeiro play no playbook faz o seguinte:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Instala o Docker, o runtime de container.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Inicia o serviço do Docker.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Desativa o SELinux, uma vez que ele ainda não é totalmente suportado pelo Kubernetes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Define alguns valores &lt;code&gt;sysctl&lt;/code&gt; relacionados ao netfilter, necessários para o trabalho em rede. Isso permitirá que o Kubernetes defina regras de iptables para receber tráfego de rede IPv4 e IPv6 em bridge nos nodes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Adiciona o repositório YUM do Kubernetes às listas de repositórios de seus servidores remotos.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Instala &lt;code&gt;kubelet&lt;/code&gt; e &lt;code&gt;kubeadm&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;O segundo play consiste de uma única tarefa que instala o &lt;code&gt;kubectl&lt;/code&gt; no seu node master.&lt;/p&gt;

&lt;p&gt;Salve e feche o arquivo quando você tiver terminado.&lt;/p&gt;

&lt;p&gt;A seguir, execute o playbook:&lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;ansible-playbook -i hosts ~/kube-cluster/kube-dependencies.yml
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Na conclusão, você verá uma saída semelhante à seguinte:&lt;/p&gt;
&lt;pre class="code-pre  local-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;PLAY [all] ****

TASK [Gathering Facts] ****
ok: [worker1]
ok: [worker2]
ok: [master]

TASK [install Docker] ****
changed: [master]
changed: [worker1]
changed: [worker2]

TASK [disable SELinux] ****
changed: [master]
changed: [worker1]
changed: [worker2]

TASK [disable SELinux on reboot] ****
changed: [master]
changed: [worker1]
changed: [worker2]

TASK [ensure net.bridge.bridge-nf-call-ip6tables is set to 1] ****
changed: [master]
changed: [worker1]
changed: [worker2]

TASK [ensure net.bridge.bridge-nf-call-iptables is set to 1] ****
changed: [master]
changed: [worker1]
changed: [worker2]

TASK [start Docker] ****
changed: [master]
changed: [worker1]
changed: [worker2]

TASK [add Kubernetes' YUM repository] *****
changed: [master]
changed: [worker1]
changed: [worker2]

TASK [install kubelet] *****
changed: [master]
changed: [worker1]
changed: [worker2]

TASK [install kubeadm] *****
changed: [master]
changed: [worker1]
changed: [worker2]

TASK [start kubelet] ****
changed: [master]
changed: [worker1]
changed: [worker2]

PLAY [master] *****

TASK [Gathering Facts] *****
ok: [master]

TASK [install kubectl] ******
ok: [master]

PLAY RECAP ****
master                     : ok=9    changed=5    unreachable=0    failed=0   
worker1                    : ok=7    changed=5    unreachable=0    failed=0  
worker2                    : ok=7    changed=5    unreachable=0    failed=0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Após a execução, o Docker, o &lt;code&gt;kubeadm&lt;/code&gt; e o &lt;code&gt;kubelet&lt;/code&gt; estarão instalados em todos os seus servidores remotos. O &lt;code&gt;kubectl&lt;/code&gt; não é um componente obrigatório e somente é necessário para a execução de comandos de cluster. A instalação dele somente no node master faz sentido nesse contexto, uma vez que você irá executar comandos &lt;code&gt;kubectl&lt;/code&gt; somente a partir do master. Contudo, observe que os comandos &lt;code&gt;kubectl&lt;/code&gt; podem ser executados a partir de quaisquer nodes worker ou a partir de qualquer máquina onde ele possa ser instalado e configurado para apontar para um cluster.&lt;/p&gt;

&lt;p&gt;Todas as dependências de sistema agora estão instaladas. Vamos configurar o node master e inicializar o cluster.&lt;/p&gt;

&lt;h2 id="passo-4-—-configurando-o-node-master"&gt;Passo 4 — Configurando o Node Master&lt;/h2&gt;

&lt;p&gt;Nesta seção, você irá configurar o node master. Antes da criação de quaisquer playbooks, contudo, vale a pena cobrir alguns conceitos como &lt;em&gt;Pods&lt;/em&gt; e &lt;em&gt;Plugins de Rede do Pod&lt;/em&gt;, uma vez que seu cluster incluirá ambos.&lt;/p&gt;

&lt;p&gt;Um pod é uma unidade atômica que executa um ou mais containers. Esses containers compartilham recursos tais como volumes de arquivo e interfaces de rede em comum. Os pods são a unidade básica de scheduling no Kubernetes: todos os containers em um pod têm a garantia de serem executados no mesmo node no qual foi feito o scheduling do pod.&lt;/p&gt;

&lt;p&gt;Cada pod tem seu próprio endereço IP, e um pod em um node deve ser capaz de acessar um pod em outro node utilizando o IP do pod. Os containers em um único node podem se comunicar facilmente através de uma interface local. Contudo, a comunicação entre pods é mais complicada e requer um componente de rede separado que possa encaminhar o tráfego de maneira transparente de um pod em um node para um pod em outro node.&lt;/p&gt;

&lt;p&gt;Essa funcionalidade é fornecida pelos plugins de rede para pods. Para este cluster vamos utilizar o &lt;a href="https://github.com/coreos/flannel"&gt;Flannel&lt;/a&gt;, uma opção estável e de bom desempenho.&lt;/p&gt;

&lt;p&gt;Crie um playbook Ansible chamado &lt;code&gt;master.yml&lt;/code&gt; em sua máquina local:&lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;vi ~/kube-cluster/master.yml
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Adicione o seguinte play ao arquivo para inicializar o cluster e instalar o Flannel:&lt;/p&gt;
&lt;div class="code-label " title="~/kube-cluster/master.yml"&gt;~/kube-cluster/master.yml&lt;/div&gt;&lt;pre class="code-pre  local-environment"&gt;&lt;code langs=""&gt;
- hosts: master
  become: yes
  tasks:
    - name: initialize the cluster
      shell: kubeadm init --pod-network-cidr=10.244.0.0/16 &amp;gt;&amp;gt; cluster_initialized.txt
      args:
        chdir: $HOME
        creates: cluster_initialized.txt

    - name: create .kube directory
      become: yes
      become_user: centos
      file:
        path: $HOME/.kube
        state: directory
        mode: 0755

    - name: copy admin.conf to user's kube config
      copy:
        src: /etc/kubernetes/admin.conf
        dest: /home/centos/.kube/config
        remote_src: yes
        owner: centos

    - name: install Pod network
      become: yes
      become_user: centos
      shell: kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/v0.9.1/Documentation/kube-flannel.yml &amp;gt;&amp;gt; pod_network_setup.txt
      args:
        chdir: $HOME
        creates: pod_network_setup.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Aqui está um detalhamento deste play:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;A primeira tarefa inicializa o cluster executando &lt;code&gt;kubeadm init&lt;/code&gt;. A passagem do argumento &lt;code&gt;--pod-network-cidr=10.244.0.0/16&lt;/code&gt; especifica a sub-rede privada que os IPs do pod serão atribuídos. O Flannel utiliza a sub-rede acima por padrão; estamos dizendo ao &lt;code&gt;kubeadm&lt;/code&gt; para utilizar a mesma sub-rede.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A segunda tarefa cria um diretório &lt;code&gt;.kube&lt;/code&gt; em &lt;code&gt;/home/centos&lt;/code&gt;. Este diretório irá manter as informações de configuração tais como os arquivos de chaves do admin, que são requeridas para conectar no cluster, e o endereço da API do cluster.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A terceira tarefa copia o arquivo &lt;code&gt;/etc/kubernetes/admin.conf&lt;/code&gt; que foi gerado a partir do &lt;code&gt;kubeadm init&lt;/code&gt; para o diretório home do seu usuário não-root &lt;strong&gt;centos&lt;/strong&gt;. Isso irá permitir que você utilize o &lt;code&gt;kubectl&lt;/code&gt; para acessar o cluster recém-criado.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A última tarefa executa &lt;code&gt;kubectl apply&lt;/code&gt; para instalar o &lt;code&gt;Flannel&lt;/code&gt;. &lt;code&gt;kubectl apply -f descriptor.[yml|json]&lt;/code&gt; é a sintaxe para dizer ao &lt;code&gt;kubectl&lt;/code&gt; para criar os objetos descritos no arquivo &lt;code&gt;descriptor.[yml|json]&lt;/code&gt;. O arquivo  &lt;code&gt;kube-flannel.yml&lt;/code&gt; contém as descrições dos objetos requeridos para a configuração do &lt;code&gt;Flannel&lt;/code&gt; no cluster.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Salve e feche o arquivo quando você tiver terminado.&lt;/p&gt;

&lt;p&gt;Execute o playbook:&lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;ansible-playbook -i hosts ~/kube-cluster/master.yml
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Na conclusão, você verá uma saída semelhante à seguinte:&lt;/p&gt;
&lt;pre class="code-pre  local-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;
PLAY [master] ****

TASK [Gathering Facts] ****
ok: [master]

TASK [initialize the cluster] ****
changed: [master]

TASK [create .kube directory] ****
changed: [master]

TASK [copy admin.conf to user's kube config] *****
changed: [master]

TASK [install Pod network] *****
changed: [master]

PLAY RECAP ****
master                     : ok=5    changed=4    unreachable=0    failed=0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Para verificar o status do node master, faça SSH nele com o seguinte comando:&lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;ssh centos@&lt;span class="highlight"&gt;master_ip&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Uma vez dentro do node master, execute:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl get nodes
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Agora você verá a seguinte saída:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;NAME      STATUS    ROLES     AGE       VERSION
master    Ready     master    1d        v1.10.1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A saída informa que o node &lt;code&gt;master&lt;/code&gt; concluiu todas as tarefas de inicialização e está em um estado &lt;code&gt;Ready&lt;/code&gt; do qual pode começar a aceitar nodes worker e executar tarefas enviadas ao Servidor de API. Agora você pode adicionar os workers a partir de sua máquina local.&lt;/p&gt;

&lt;h2 id="passo-5-—-configurando-os-nodes-worker"&gt;Passo 5 — Configurando os Nodes Worker&lt;/h2&gt;

&lt;p&gt;A adição de workers ao cluster envolve a execução de um único comando em cada um. Este comando inclui as informações necessárias sobre o cluster, tais como o endereço IP e a porta do Servidor de API do master, e um token seguro. Somentes os nodes que passam no token seguro estarão aptos a ingressar no cluster.&lt;/p&gt;

&lt;p&gt;Navegue de volta para a sua área de trabalho e crie um playbook chamado &lt;code&gt;workers.yml&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;vi ~/kube-cluster/workers.yml
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Adicione o seguinte texto ao arquivo para adicionar os workers ao cluster:&lt;/p&gt;
&lt;div class="code-label " title="~/kube-cluster/workers.yml"&gt;~/kube-cluster/workers.yml&lt;/div&gt;&lt;pre class="code-pre  local-environment"&gt;&lt;code langs=""&gt;
- hosts: master
  become: yes
  gather_facts: false
  tasks:
    - name: get join command
      shell: kubeadm token create --print-join-command
      register: join_command_raw

    - name: set join command
      set_fact:
        join_command: "{{ join_command_raw.stdout_lines[0] }}"


- hosts: workers
  become: yes
  tasks:
    - name: join cluster
      shell: "{{ hostvars['master'].join_command }} &amp;gt;&amp;gt; node_joined.txt"
      args:
        chdir: $HOME
        creates: node_joined.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Aqui está o que o playbook faz:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;O primeiro play obtém o comando de junção que precisa ser executado nos nodes workers. Este comando estará no seguinte formato: &lt;code&gt;kubeadm join --token &amp;lt;token&amp;gt; &amp;lt;master-ip&amp;gt;:&amp;lt;master-port&amp;gt; --discovery-token-ca-cert-hash sha256:&amp;lt;hash&amp;gt;&lt;/code&gt;. Assim que obtiver o comando real com os valores apropriados de &lt;strong&gt;token&lt;/strong&gt; e &lt;strong&gt;hash&lt;/strong&gt;, a tarefa define isso como um fact para que o próximo play possa acessar essa informação.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;O segundo play tem uma única tarefa que executa o comando de junção em todos os nodes worker. Na conclusão desta tarefa, os dois nodes worker farão parte do cluster.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Salve e feche o arquivo quando você tiver terminado.&lt;/p&gt;

&lt;p&gt;Execute o playbook:&lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;ansible-playbook -i hosts ~/kube-cluster/workers.yml
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Na conclusão, você verá uma saída semelhante à seguinte:&lt;/p&gt;
&lt;pre class="code-pre  local-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;PLAY [master] ****

TASK [get join command] ****
changed: [master]

TASK [set join command] *****
ok: [master]

PLAY [workers] *****

TASK [Gathering Facts] *****
ok: [worker1]
ok: [worker2]

TASK [join cluster] *****
changed: [worker1]
changed: [worker2]

PLAY RECAP *****
master                     : ok=2    changed=1    unreachable=0    failed=0   
worker1                    : ok=2    changed=1    unreachable=0    failed=0  
worker2                    : ok=2    changed=1    unreachable=0    failed=0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Com a adição dos nodes worker, seu cluster está agora totalmente configurado e funcional, com os workers prontos para executar cargas de trabalho. Antes de fazer o scheduling de aplicações, vamos verificar se o cluster está funcionando conforme o esperado. &lt;/p&gt;

&lt;h2 id="step-6-—-verificando-o-cluster"&gt;Step 6 — Verificando o Cluster&lt;/h2&gt;

&lt;p&gt;Às vezes, um cluster pode falhar durante a configuração porque um node está inativo ou a conectividade de rede entre o master e o worker não está funcionando corretamente. Vamos verificar o cluster e garantir que os nodes estejam operando corretamente.&lt;/p&gt;

&lt;p&gt;Você precisará verificar o estado atual do cluster a partir do node master para garantir que os nodes estejam prontos. Se você se desconectou do node master, pode voltar e fazer SSH com o seguinte comando:&lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;ssh centos@&lt;span class="highlight"&gt;master_ip&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Em seguida, execute o seguinte comando para obter o status do cluster:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl get nodes
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Você verá uma saída semelhante à seguinte:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;NAME      STATUS    ROLES     AGE       VERSION
master    Ready     master    1d        v1.10.1
worker1   Ready     &amp;lt;none&amp;gt;    1d        v1.10.1 
worker2   Ready     &amp;lt;none&amp;gt;    1d        v1.10.1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Se todos os seus nodes têm o valor &lt;code&gt;Ready&lt;/code&gt; para o &lt;code&gt;STATUS&lt;/code&gt;, significa que eles são parte do cluster e estão prontos para executar cargas de trabalho.&lt;/p&gt;

&lt;p&gt;Se, contudo, alguns dos nodes têm &lt;code&gt;NotReady&lt;/code&gt; como o &lt;code&gt;STATUS&lt;/code&gt;, isso pode significar que os nodes worker ainda não concluíram sua configuração. Aguarde cerca de cinco a dez minutos antes de voltar a executar &lt;code&gt;kubectl get nodes&lt;/code&gt; e fazer a inspeção da nova saída. Se alguns nodes ainda têm &lt;code&gt;NotReady&lt;/code&gt; como status, talvez seja necessário verificar e executar novamente os comandos nas etapas anteriores.  &lt;/p&gt;

&lt;p&gt;Agora que seu cluster foi verificado com sucesso, vamos fazer o scheduling de um exemplo de aplicativo Nginx no cluster.&lt;/p&gt;

&lt;h2 id="passo-7-—-executando-uma-aplicação-no-cluster"&gt;Passo 7 — Executando Uma Aplicação no Cluster&lt;/h2&gt;

&lt;p&gt;Você pode fazer o deploy de qualquer aplicação containerizada no seu cluster. Para manter as coisas familiares, vamos fazer o deploy do Nginx utilizando &lt;em&gt;Deployments&lt;/em&gt; e &lt;em&gt;Services&lt;/em&gt; para ver como pode ser feito o deploy dessa aplicação no cluster. Você também pode usar os comandos abaixo para outros aplicativos em container, desde que você altere o nome da imagem do Docker e quaisquer flags relevantes (tais como &lt;code&gt;ports&lt;/code&gt; e &lt;code&gt;volumes&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Ainda no node master, execute o seguinte comando para criar um deployment chamado &lt;code&gt;nginx&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl run &lt;span class="highlight"&gt;nginx&lt;/span&gt; --image=&lt;span class="highlight"&gt;nginx&lt;/span&gt; --port &lt;span class="highlight"&gt;80&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Um deployment é um tipo de objeto do Kubernetes que garante que há sempre um número especificado de pods em execução com base em um modelo definido, mesmo se o pod falhar durante o tempo de vida do cluster. O deployment acima irá criar um pod com um container do registro do Docker &lt;a href="https://hub.docker.com/_/nginx/"&gt;Nginx Docker Image&lt;/a&gt;.  &lt;/p&gt;

&lt;p&gt;A seguir, execute o seguinte comando para criar um serviço chamado &lt;code&gt;nginx&lt;/code&gt; que irá expor o app publicamente. Ele fará isso por meio de um &lt;em&gt;NodePort&lt;/em&gt;, um esquema que tornará o pod acessível através de uma porta arbitrária aberta em cada node do cluster: &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl expose deploy &lt;span class="highlight"&gt;nginx&lt;/span&gt; --port &lt;span class="highlight"&gt;80 --target-port &lt;/span&gt;80&amp;lt;^&amp;gt; --type NodePort
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Services são outro tipo de objeto do Kubernetes que expõe serviços internos do cluster para os clientes, tanto internos quanto externos. Eles também são capazes de fazer balanceamento de solicitações para vários pods e são um componente integral no Kubernetes, interagindo frequentemente com outros componentes.&lt;/p&gt;

&lt;p&gt;Execute o seguinte comando:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl get services
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Isso produzirá uma saída semelhante à seguinte:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;NAME         TYPE        CLUSTER-IP       EXTERNAL-IP           PORT(S)        AGE
kubernetes   ClusterIP   10.96.0.1        &amp;lt;none&amp;gt;                443/TCP        1d
&lt;span class="highlight"&gt;nginx&lt;/span&gt;        NodePort    10.109.228.209   &amp;lt;none&amp;gt;                80:&lt;span class="highlight"&gt;nginx_port&lt;/span&gt;/TCP   40m
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A partir da terceira linha da saída acima, você pode obter a porta em que o Nginx está sendo executado. O Kubernetes atribuirá uma porta aleatória maior que &lt;code&gt;30000&lt;/code&gt; automaticamente, enquanto garante que a porta já não esteja vinculada a outro serviço.&lt;/p&gt;

&lt;p&gt;Para testar se tudo está funcionando, visite &lt;code&gt;http://&lt;span class="highlight"&gt;worker_1_ip&lt;/span&gt;:&lt;span class="highlight"&gt;nginx_port&lt;/span&gt;&lt;/code&gt; ou &lt;code&gt;http://&lt;span class="highlight"&gt;worker_2_ip&lt;/span&gt;:&lt;span class="highlight"&gt;nginx_port&lt;/span&gt;&lt;/code&gt; através de um navegador na sua máquina local. Você verá a familiar página de boas-vindas do Nginx.&lt;/p&gt;

&lt;p&gt;Se você quiser remover o aplicativo Nginx, primeiro exclua o serviço &lt;code&gt;nginx&lt;/code&gt; do node master:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl delete service &lt;span class="highlight"&gt;nginx&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Execute o seguinte para garantir que o serviço tenha sido excluído:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl get services
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Você verá a seguinte saída:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;NAME         TYPE        CLUSTER-IP       EXTERNAL-IP           PORT(S)        AGE
kubernetes   ClusterIP   10.96.0.1        &amp;lt;none&amp;gt;                443/TCP        1d
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Para excluir o deployment:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;kubectl delete deployment &lt;span class="highlight"&gt;nginx&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Execute o seguinte para confirmar que isso funcionou:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl get deployments
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;No resources found.
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="conclusão"&gt;Conclusão&lt;/h2&gt;

&lt;p&gt;Neste guia, você configurou com sucesso um cluster do Kubernetes no CentOS 7 usando Kubeadm e Ansible para automação.&lt;/p&gt;

&lt;p&gt;Se você está se perguntando o que fazer com o cluster, agora que ele está configurado, um bom próximo passo seria sentir-se confortável para implantar suas próprias aplicações e serviços no cluster. Aqui está uma lista de links com mais informações que podem orientá-lo no processo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://docs.docker.com/engine/examples/"&gt;Dockerizing applications&lt;/a&gt; - lista exemplos que detalham como containerizar aplicações usando o Docker.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://kubernetes.io/docs/concepts/workloads/pods/pod-overview/"&gt;Pod Overview&lt;/a&gt; - descreve em detalhes como os Pods funcionam e seu relacionamento com outros objetos do Kubernetes. Os pods são onipresentes no Kubernetes, então compreendê-los facilitará seu trabalho.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://kubernetes.io/docs/concepts/workloads/controllers/deployment/"&gt;Deployments Overview&lt;/a&gt; - fornece uma visão geral dos deployments. É útil entender como os controladores, como os deployments, funcionam, pois eles são usados com frequência em aplicações stateless para escalonamento e na recuperação automatizada de aplicações não íntegras.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://kubernetes.io/docs/concepts/services-networking/service/"&gt;Services Overview&lt;/a&gt; - cobre os serviços ou services, outro objeto frequentemente usado em clusters do Kubernetes. Entender os tipos de serviços e as opções que eles têm é essencial para executar aplicações stateless e stateful.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Outros conceitos importantes que você pode analisar são &lt;a href="https://kubernetes.io/docs/concepts/storage/volumes/"&gt;Volumes&lt;/a&gt;, &lt;a href="https://kubernetes.io/docs/concepts/services-networking/ingress/"&gt;Ingresses&lt;/a&gt; e &lt;a href="https://kubernetes.io/docs/concepts/configuration/secret/"&gt;Secrets&lt;/a&gt;, os quais são úteis ao realizar o deploy de aplicações em produção. &lt;/p&gt;

&lt;p&gt;O Kubernetes tem muitas funcionalidades e recursos a oferecer. &lt;a href="https://kubernetes.io/docs/"&gt;A Documentação Oficial do Kubernetes&lt;/a&gt; é o melhor lugar para aprender sobre conceitos, encontrar guias específicos de tarefas e procurar referências de API para vários objetos.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Por bsder&lt;/em&gt;&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/como-criar-um-cluster-kubernetes-1-11-usando-kubeadm-no-ubuntu-18-04-pt</id>
    <published>2018-09-11T00:44:21Z</published>
    <updated>2018-09-11T00:47:12Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/como-criar-um-cluster-kubernetes-1-11-usando-kubeadm-no-ubuntu-18-04-pt"/>
    <title>Como Criar um Cluster Kubernetes 1.11 Usando Kubeadm no Ubuntu 18.04</title>
    <content type="html">&lt;p&gt;&lt;em&gt;O autor escolheu o &lt;a href="https://www.brightfunds.org/funds/foss-nonprofits"&gt;Free and Open Source Fund&lt;/a&gt; para receber uma doação como parte do programa &lt;a href="https://do.co/w4do-cta"&gt;Write for DOnations&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h3 id="introdução"&gt;Introdução&lt;/h3&gt;

&lt;p&gt;O &lt;a href="https://kubernetes.io/"&gt;Kubernetes&lt;/a&gt; é um sistema de orquestração de container em escala. Inicialmente desenvolvido pelo Google baseado em suas experiências executando containers em produção. O Kubernetes é open source e desenvolvido ativamente por uma comunidade em todo o mundo.&lt;/p&gt;

&lt;p&gt;O &lt;a href="https://kubernetes.io/docs/reference/setup-tools/kubeadm/kubeadm/"&gt;Kubeadm&lt;/a&gt; atomatiza a instalação e a configuração de componentes do Kubernetes tais como o servidor de API, o Controller Manager, e o Kube DNS. Contudo, ele não cria usuários ou lida com a instalação de dependências no nível do sistema operacional e sua configuração. Para essa tarefas preliminares, é possível utilizar uma ferramenta de gerência de configuração como o &lt;a href="https://www.ansible.com/"&gt;Ansible&lt;/a&gt; ou o &lt;a href="https://saltstack.com/"&gt;SaltStack&lt;/a&gt;. A utilização dessas ferramentas torna a criação de clusters adicionais ou a recriação de clusters existentes muito mais simples e menos propensa a erros.&lt;/p&gt;

&lt;p&gt;Neste guia, você vai configurar um cluster Kubernetes a partir do zero utilizando o Ansible e o Kubeadm, e a seguir fazer o deploy de uma aplicação Nginx containerizada nele.&lt;/p&gt;

&lt;h2 id="objetivos"&gt;Objetivos&lt;/h2&gt;

&lt;p&gt;Seu cluster irá incluir os seguintes recursos físicos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Um node master&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;O node master (um &lt;em&gt;node&lt;/em&gt; no Kubernetes refere-se a um servidor) é responsável por gerenciar o estado do cluster. Ele roda o  &lt;a href="https://github.com/coreos/etcd"&gt;Etcd&lt;/a&gt;, que armazena dados de cluster entre componentes que fazem o scheduling de cargas de trabalho para nodes worker ou nodes de trabalho. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Dois nodes worker&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nodes worker são os servidores onde suas &lt;em&gt;cargas de trabalho&lt;/em&gt; (i.e. aplicações e serviços containerizados) irão executar. Um worker continuará a executar sua carga de trabalho uma vez que estejam atribuídos a ela, mesmo se o master for desativado quando o scheduling estiver concluído. A capacidade de um cluster pode ser aumentada adicionando workers.&lt;/p&gt;

&lt;p&gt;Após a conclusão desse guia, você terá um cluster pronto para executar aplicações containerizadas, desde que os servidores no cluster tenham recursos suficientes de CPU e RAM para suas aplicações consumirem. Quase todas as aplicações Unix tradicionais, incluindo aplicações web, bancos de dados, daemons, e ferramentas de linha de comando podem ser containerizadas e feitas para rodar no cluster. O cluster em si consumirá cerca de 300-500MB de memória e 10% de CPU em cada node.&lt;/p&gt;

&lt;p&gt;Uma vez que o cluster esteja configurado, você fará o deploy do servidor web &lt;a href="https://nginx.org/en/"&gt;Nginx&lt;/a&gt; nele para assegurar que ele está executando as cargas de trabalho corretamente.&lt;/p&gt;

&lt;h2 id="pré-requisitos"&gt;Pré-requisitos&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Um par de chaves SSH em sua máquina Linux/macOS/BSD local. Se você não tiver usado chaves SSH antes, você pode aprender como configurá-las seguindo &lt;a href="https://www.digitalocean.com/community/tutorials/ssh-essentials-working-with-ssh-servers-clients-and-keys#generating-and-working-with-ssh-keys"&gt;esta explicação em como configurar chaves SSH em sua máquina local&lt;/a&gt;. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Três servidores rodando Ubuntu 18.04 com pelo menos 1GB de RAM. Você deve ser capaz de fazer SSH em cada servidor como usuário root com o seu par de chaves SSH. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Ansible instalado em sua máquina local. Se você estiver executando Ubuntu 18.04 como seu SO, siga o a seção "Passo 1 - Instalando o Ansible" em &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-and-configure-ansible-on-ubuntu-18-04#step-1-%E2%80%94-installing-ansible"&gt;Como instalar e configurar o Ansible no Ubuntu 18.04&lt;/a&gt; para instalar o Ansible. Para instruções de instalações em outras plataformas como o macOS ou CentOS, siga a &lt;a href="http://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html#installing-the-control-machine"&gt;documentação oficial da instalação do Ansible&lt;/a&gt;. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Familiaridade com o playbooks do Ansible. Para uma revisão, verifique &lt;a href="https://www.digitalocean.com/community/tutorials/configuration-management-101-writing-ansible-playbooks"&gt;Configuration Management 101: Writing Ansible Playbooks&lt;/a&gt;. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Conhecimento de como lançar um container a partir de uma imagem Docker. Dê uma olhada no "Passo 5 — Executando um Container Docker" em  &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-and-use-docker-on-ubuntu-18-04#step-5-%E2%80%94-running-a-docker-container"&gt;Como instalar e usar o Docker no Ubuntu 18.04&lt;/a&gt; se precisar relembrar.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="passo-1-—-configurando-o-diretório-da-Área-de-trabalho-e-o-arquivo-de-inventário-ansible"&gt;Passo 1 — Configurando o Diretório da Área de Trabalho e o Arquivo de Inventário Ansible&lt;/h2&gt;

&lt;p&gt;Nessa seção, você vai criar um diretório em sua máquina local que irá servir como sua área de trabalho. Você configurará o Ansible localmente para que ele possa se comunicar e executar comandos em seus servidores remotos. Depois disso pronto, você irá criar um arquivo &lt;code&gt;hosts&lt;/code&gt; contendo informações de inventário tais como os endereços IP de seus servidores e os grupos aos quais cada servidor pertence. &lt;/p&gt;

&lt;p&gt;Dos seus três servidores, um será o master com um IP exibido como &lt;code&gt;&lt;span class="highlight"&gt;master_ip&lt;/span&gt;&lt;/code&gt;. Os outros dois servidores serão workers e terão os IPs &lt;code&gt;&lt;span class="highlight"&gt;worker_1_ip&lt;/span&gt;&lt;/code&gt; e &lt;code&gt;&lt;span class="highlight"&gt;worker_2_ip&lt;/span&gt;&lt;/code&gt;.  &lt;/p&gt;

&lt;p&gt;Crie um diretório chamado &lt;code&gt;~/kube-cluster&lt;/code&gt; no diretório home de sua máquina local e faça um &lt;code&gt;cd&lt;/code&gt; para dentro dele:&lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;mkdir ~/kube-cluster
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;cd ~/kube-cluster
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Esse diretório será sua área de trabalho para o restante desse tutorial e conterá todos os seus playbooks de Ansible. Ele também será o diretório no qual você irá executar todos os comandos locais.&lt;/p&gt;

&lt;p&gt;Crie um arquivo chamado &lt;code&gt;~/kube-cluster/hosts&lt;/code&gt; usando o &lt;code&gt;nano&lt;/code&gt; ou o seu editor de textos favorito: &lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nano ~/kube-cluster/hosts
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Adicione o seguinte texto ao arquivo, que irá especificar informações sobre a estrutura lógica do cluster:&lt;/p&gt;
&lt;div class="code-label " title="~/kube-cluster/hosts"&gt;~/kube-cluster/hosts&lt;/div&gt;&lt;pre class="code-pre  local-environment"&gt;&lt;code langs=""&gt;
[masters]
master ansible_host=&lt;span class="highlight"&gt;master_ip&lt;/span&gt; ansible_user=root

[workers]
worker1 ansible_host=&lt;span class="highlight"&gt;worker_1_ip&lt;/span&gt; ansible_user=root
worker2 ansible_host=&lt;span class="highlight"&gt;worker_2_ip&lt;/span&gt; ansible_user=root

[all:vars]
ansible_python_interpreter=/usr/bin/python3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Você deve se lembrar de que &lt;a href="http://docs.ansible.com/ansible/latest/user_guide/intro_inventory.html"&gt;&lt;em&gt;arquivos de inventário&lt;/em&gt;&lt;/a&gt; no Ansible são utilizados para especificar informações de servidor tais como endereços IP, usuários remotos, e agrupamentos de servidores para tratar como uma unidade única para a execução de comandos. O &lt;code&gt;~/kube-cluster/hosts&lt;/code&gt; será o seu arquivo de inventário e você adicionou dois grupos Ansible a ele (&lt;strong&gt;masters&lt;/strong&gt; e &lt;strong&gt;workers&lt;/strong&gt;) especificando a estrutura lógica do seu cluster.&lt;/p&gt;

&lt;p&gt;No grupo &lt;strong&gt;masters&lt;/strong&gt;, existe uma entrada de servidor chamada "master" que lista o IP do node master (&lt;code&gt;&lt;span class="highlight"&gt;master_ip&lt;/span&gt;&lt;/code&gt;) e especifica que o Ansible deve executar comandos remotos como root.&lt;/p&gt;

&lt;p&gt;De maneira similar, no grupo &lt;strong&gt;workers&lt;/strong&gt;, existem duas entradas para os servidores workers (&lt;code&gt;&lt;span class="highlight"&gt;worker_1_ip&lt;/span&gt;&lt;/code&gt; e &lt;code&gt;&lt;span class="highlight"&gt;worker_2_ip&lt;/span&gt;&lt;/code&gt;) que também especificam o &lt;code&gt;ansible_user&lt;/code&gt; como root.&lt;/p&gt;

&lt;p&gt;A última linha do arquivo diz ao Ansible para utilizar os intepretadores Python dos servidores remotos para suas operações de gerenciamento.&lt;/p&gt;

&lt;p&gt;Salve e feche o arquivo depois de ter adicionado o texto.&lt;/p&gt;

&lt;p&gt;Tendo configurado o inventário do servidor com grupos, vamos passar a instalar dependências no nível do sistema operacional e a criar definições de configuração.&lt;/p&gt;

&lt;h2 id="passo-2-—-criando-um-usuário-não-root-em-todos-os-servidores-remotos"&gt;Passo 2 — Criando um Usuário Não-Root em Todos os Servidores Remotos&lt;/h2&gt;

&lt;p&gt;Nesta seção você irá criar um usuário não-root com privilégios sudo em todos os servidores para que você possa fazer SSH manualmente neles como um usuário sem privilégios. Isso pode ser útil se, por exemplo, você gostaria de ver informações do sistema com comandos como &lt;code&gt;top/htop&lt;/code&gt;, ver a lista de containers em execução, ou alterar arquivos de configuração de propriedade do root. Estas operações são rotineiramente executadas durante a manutenção de um cluster, e a utilização de um usuário que não seja root para tarefas desse tipo minimiza o risco de modificação ou exclusão de arquivos importantes ou a realização não intencional de operações perigosas.&lt;/p&gt;

&lt;p&gt;Crie um arquivo chamado &lt;code&gt;~/kube-cluster/initial.yml&lt;/code&gt; na área de trabalho:&lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nano ~/kube-cluster/initial.yml
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A seguir, adicione o seguinte play ao arquivo para criar um usuário não-root com privilégios sudo em todos os servidores. Um play no Ansible é uma coleção de passos a serem realizados que visam servidores e grupos específicos. O seguinte play irá criar um usuário sudo não-root:&lt;/p&gt;
&lt;div class="code-label " title="~/kube-cluster/initial.yml"&gt;~/kube-cluster/initial.yml&lt;/div&gt;&lt;pre class="code-pre  local-environment"&gt;&lt;code langs=""&gt;
- hosts: all
  become: yes
  tasks:
    - name: create the 'ubuntu' user
      user: name=ubuntu append=yes state=present createhome=yes shell=/bin/bash

    - name: allow 'ubuntu' to have passwordless sudo
      lineinfile:
        dest: /etc/sudoers
        line: 'ubuntu ALL=(ALL) NOPASSWD: ALL'
        validate: 'visudo -cf %s'

    - name: set up authorized keys for the ubuntu user
      authorized_key: user=ubuntu key="{{item}}"
      with_file:
        - ~/.ssh/id_rsa.pub
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Aqui está um detalhamento do que este playbook faz:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Cria um usuário não-root &lt;code&gt;ubuntu&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Configura o arquivo &lt;code&gt;sudoers&lt;/code&gt; para permitir o usuário &lt;code&gt;ubuntu&lt;/code&gt; executar comandos &lt;code&gt;sudo&lt;/code&gt; sem uma solicitação de senha.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Adiciona a chave pública em sua máquina local (normalmente &lt;code&gt;~/.ssh/id_rsa.pub&lt;/code&gt;) para a lista de chaves autorizadas do usuário remoto &lt;code&gt;ubuntu&lt;/code&gt;. Isto o permitirá fazer SSH para dentro de cada servidor como usuário &lt;code&gt;ubuntu&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Salve e feche o arquivo depois que tiver adicionado o texto.&lt;/p&gt;

&lt;p&gt;Em seguida, rode o playbook localmente executando:&lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;ansible-playbook -i hosts ~/kube-cluster/initial.yml
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;O comando será concluído dentro de dois a cinco minutos. Na conclusão, você verá uma saída semelhante à seguinte:&lt;/p&gt;
&lt;pre class="code-pre  local-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;PLAY [all] ****

TASK [Gathering Facts] ****
ok: [master]
ok: [worker1]
ok: [worker2]

TASK [create the 'ubuntu' user] ****
changed: [master]
changed: [worker1]
changed: [worker2]

TASK [allow 'ubuntu' user to have passwordless sudo] ****
changed: [master]
changed: [worker1]
changed: [worker2]

TASK [set up authorized keys for the ubuntu user] ****
changed: [worker1] =&amp;gt; (item=ssh-rsa AAAAB3...)
changed: [worker2] =&amp;gt; (item=ssh-rsa AAAAB3...)
changed: [master] =&amp;gt; (item=ssh-rsa AAAAB3...)

PLAY RECAP ****
master                     : ok=5    changed=4    unreachable=0    failed=0   
worker1                    : ok=5    changed=4    unreachable=0    failed=0   
worker2                    : ok=5    changed=4    unreachable=0    failed=0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Agora que a configuração preliminar está completa, você pode passar para a instalação de dependências específicas do Kubernetes. &lt;/p&gt;

&lt;h2 id="step-3-—-instalando-as-dependências-do-kubernetes"&gt;Step 3 — Instalando as Dependências do Kubernetes&lt;/h2&gt;

&lt;p&gt;Nesta seção, você irá instalar os pacotes no nível do sistema operacional necessários pelo Kubernetes com o gerenciador de pacotes do Ubuntu. Esses pacotes são:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Docker - um runtime de container. Este é o componente que executa seus containers. Suporte a outros runtimes como o &lt;a href="https://coreos.com/rkt/"&gt;rkt&lt;/a&gt; está em desenvolvimento ativo no Kubernetes. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;kubeadm&lt;/code&gt; - uma ferramenta CLI que irá instalar e configurar os vários componentes de um cluster de uma maneira padrão.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;kubelet&lt;/code&gt; - um serviço/programa de sistema que roda em todos os nodes e lida com operações no nível do node.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;kubectl&lt;/code&gt; - uma ferramenta CLI usada para emitir comandos para o cluster através de seu servidor de API.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Crie um arquivo chamado &lt;code&gt;~/kube-cluster/kube-dependencies.yml&lt;/code&gt; na área de trabalho:&lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nano ~/kube-cluster/kube-dependencies.yml
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Adicione os seguintes plays ao arquivo para instalar esses pacotes em seus servidores:&lt;/p&gt;
&lt;div class="code-label " title="~/kube-cluster/kube-dependencies.yml"&gt;~/kube-cluster/kube-dependencies.yml&lt;/div&gt;&lt;pre class="code-pre  local-environment"&gt;&lt;code langs=""&gt;
- hosts: all
  become: yes
  tasks:
   - name: install Docker
     apt:
       name: docker.io
       state: present
       update_cache: true

   - name: install APT Transport HTTPS
     apt:
       name: apt-transport-https
       state: present

   - name: add Kubernetes apt-key
     apt_key:
       url: https://packages.cloud.google.com/apt/doc/apt-key.gpg
       state: present

   - name: add Kubernetes' APT repository
     apt_repository:
      repo: deb http://apt.kubernetes.io/ kubernetes-xenial main
      state: present
      filename: 'kubernetes'

   - name: install kubelet
     apt:
       name: kubelet
       state: present
       update_cache: true

   - name: install kubeadm
     apt:
       name: kubeadm
       state: present

- hosts: master
  become: yes
  tasks:
   - name: install kubectl
     apt:
       name: kubectl
       state: present
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;O primeiro play no playbook faz o seguinte:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Instala o Docker, o runtime de container.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Instala o &lt;code&gt;apt-transport-https&lt;/code&gt;, permitindo que você adicione fontes HTTPS externas à sua lista de fontes do APT.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Adiciona a apt-key do repositório APT do Kubernetes para verificação de chave.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Adiciona o repositório APT do Kubernetes à lista de fontes do APT dos seus servidores remotos.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Instala &lt;code&gt;kubelet&lt;/code&gt; e &lt;code&gt;kubeadm&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;O segundo play consiste de uma única tarefa que instala o &lt;code&gt;kubectl&lt;/code&gt; no seu node master.&lt;/p&gt;

&lt;p&gt;Salve e feche o arquivo quando você tiver terminado.&lt;/p&gt;

&lt;p&gt;A seguir, rode o playbook executando localmente: &lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;ansible-playbook -i hosts ~/kube-cluster/kube-dependencies.yml
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Na conclusão, você verá uma saída semelhante à seguinte:&lt;/p&gt;
&lt;pre class="code-pre  local-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;PLAY [all] ****

TASK [Gathering Facts] ****
ok: [worker1]
ok: [worker2]
ok: [master]

TASK [install Docker] ****
changed: [master]
changed: [worker1]
changed: [worker2]

TASK [install APT Transport HTTPS] *****
ok: [master]
ok: [worker1]
changed: [worker2]

TASK [add Kubernetes apt-key] *****
changed: [master]
changed: [worker1]
changed: [worker2]

TASK [add Kubernetes' APT repository] *****
changed: [master]
changed: [worker1]
changed: [worker2]

TASK [install kubelet] *****
changed: [master]
changed: [worker1]
changed: [worker2]

TASK [install kubeadm] *****
changed: [master]
changed: [worker1]
changed: [worker2]

PLAY [master] *****

TASK [Gathering Facts] *****
ok: [master]

TASK [install kubectl] ******
ok: [master]

PLAY RECAP ****
master                     : ok=9    changed=5    unreachable=0    failed=0   
worker1                    : ok=7    changed=5    unreachable=0    failed=0  
worker2                    : ok=7    changed=5    unreachable=0    failed=0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Após a execução, o Docker, o &lt;code&gt;kubeadm&lt;/code&gt; e o &lt;code&gt;kubelet&lt;/code&gt; estarão instalados em todos os seus servidores remotos. O &lt;code&gt;kubectl&lt;/code&gt; não é um componente obrigatório e somente é necessário para a execução de comandos de cluster. A instalação dele somente no node master faz sentido nesse contexto, uma vez que você irá executar comandos &lt;code&gt;kubectl&lt;/code&gt; somente a partir do master. Contudo, observe que os comandos &lt;code&gt;kubectl&lt;/code&gt; podem ser executados a partir de quaisquer nodes worker ou a partir de qualquer máquina onde ele possa ser instalado e configurado para apontar para um cluster.&lt;/p&gt;

&lt;p&gt;Todas as dependências de sistema agora estão instaladas. Vamos configurar o node master e inicializar o cluster.&lt;/p&gt;

&lt;h2 id="passo-4-—-configurando-o-node-master"&gt;Passo 4 — Configurando o Node Master&lt;/h2&gt;

&lt;p&gt;Nesta seção, você irá configurar o node master. Antes da criação de quaisquer playbooks, contudo, vale a pena cobrir alguns conceitos como &lt;em&gt;Pods&lt;/em&gt; e &lt;em&gt;Plugins de Rede do Pod&lt;/em&gt;, uma vez que seu cluster incluirá ambos.&lt;/p&gt;

&lt;p&gt;Um pod é uma unidade atômica que executa um ou mais containers. Esses containers compartilham recursos tais como volumes de arquivo e interfaces de rede em comum. Os pods são a unidade básica de scheduling no Kubernetes: todos os containers em um pod têm a garantia de serem executados no mesmo node no qual foi feito o scheduling do pod.&lt;/p&gt;

&lt;p&gt;Cada pod tem seu próprio endereço IP, e um pod em um node deve ser capaz de acessar um pod em outro node utilizando o IP do pod. Os containers em um único node podem se comunicar facilmente através de uma interface local. Contudo, a comunicação entre pods é mais complicada e requer um componente de rede separado que possa encaminhar o tráfego de maneira transparente de um pod em um node para um pod em outro node.&lt;/p&gt;

&lt;p&gt;Essa funcionalidade é fornecida pelos plugins de rede para pods. Para este cluster vamos utilizar o &lt;a href="https://github.com/coreos/flannel"&gt;Flannel&lt;/a&gt;, uma opção estável e de bom desempenho.&lt;/p&gt;

&lt;p&gt;Crie um playbook Ansible chamado &lt;code&gt;master.yml&lt;/code&gt; em sua máquina local:&lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nano ~/kube-cluster/master.yml
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Adicione o seguinte play ao arquivo para inicializar o cluster e instalar o Flannel:&lt;/p&gt;
&lt;div class="code-label " title="~/kube-cluster/master.yml"&gt;~/kube-cluster/master.yml&lt;/div&gt;&lt;pre class="code-pre  local-environment"&gt;&lt;code langs=""&gt;
- hosts: master
  become: yes
  tasks:
    - name: initialize the cluster
      shell: kubeadm init --pod-network-cidr=10.244.0.0/16 &amp;gt;&amp;gt; cluster_initialized.txt
      args:
        chdir: $HOME
        creates: cluster_initialized.txt

    - name: create .kube directory
      become: yes
      become_user: ubuntu
      file:
        path: $HOME/.kube
        state: directory
        mode: 0755

    - name: copy admin.conf to user's kube config
      copy:
        src: /etc/kubernetes/admin.conf
        dest: /home/ubuntu/.kube/config
        remote_src: yes
        owner: ubuntu

    - name: install Pod network
      become: yes
      become_user: ubuntu
      shell: kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/v0.9.1/Documentation/kube-flannel.yml &amp;gt;&amp;gt; pod_network_setup.txt
      args:
        chdir: $HOME
        creates: pod_network_setup.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Aqui está um detalhamento deste play:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;A primeira tarefa inicializa o cluster executando &lt;code&gt;kubeadm init&lt;/code&gt;. A passagem do argumento &lt;code&gt;--pod-network-cidr=10.244.0.0/16&lt;/code&gt; especifica a sub-rede privada que os IPs do pod serão atribuídos. O Flannel utiliza a sub-rede acima por padrão; estamos dizendo ao &lt;code&gt;kubeadm&lt;/code&gt; para utilizar a mesma sub-rede.  &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A segunda tarefa cria um diretório &lt;code&gt;.kube&lt;/code&gt; em &lt;code&gt;/home/ubuntu&lt;/code&gt;. Este diretório irá manter as informações de configuração tais como os arquivos de chaves do admin, que são requeridas para conectar no cluster, e o endereço da API do cluster.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A terceira tarefa copia o arquivo &lt;code&gt;/etc/kubernetes/admin.conf&lt;/code&gt; que foi gerado a partir do &lt;code&gt;kubeadm init&lt;/code&gt; para o diretório home do seu usuário não-root. Isso irá permitir que você utilize o &lt;code&gt;kubectl&lt;/code&gt; para acessar o cluster recém-criado.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A última tarefa executa &lt;code&gt;kubectl apply&lt;/code&gt; para instalar o &lt;code&gt;Flannel&lt;/code&gt;. &lt;code&gt;kubectl apply -f descriptor.[yml|json]&lt;/code&gt; é a sintaxe para dizer ao &lt;code&gt;kubectl&lt;/code&gt; para criar os objetos descritos no arquivo &lt;code&gt;descriptor.[yml|json]&lt;/code&gt;. O arquivo  &lt;code&gt;kube-flannel.yml&lt;/code&gt; contém as descrições dos objetos requeridos para a configuração do &lt;code&gt;Flannel&lt;/code&gt; no cluster.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Salve e feche o arquivo quando você tiver terminado.&lt;/p&gt;

&lt;p&gt;Rode o playbook localmente executando: &lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;ansible-playbook -i hosts ~/kube-cluster/master.yml
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Na conclusão, você verá uma saída semelhante à seguinte:&lt;/p&gt;
&lt;pre class="code-pre  local-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;
PLAY [master] ****

TASK [Gathering Facts] ****
ok: [master]

TASK [initialize the cluster] ****
changed: [master]

TASK [create .kube directory] ****
changed: [master]

TASK [copy admin.conf to user's kube config] *****
changed: [master]

TASK [install Pod network] *****
changed: [master]

PLAY RECAP ****
master                     : ok=5    changed=4    unreachable=0    failed=0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Para verificar o status do node master, faça SSH nele com o seguinte comando:&lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;ssh ubuntu@&lt;span class="highlight"&gt;master_ip&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Uma vez dentro do node master, execute:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl get nodes
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Agora você verá a seguinte saída:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;NAME      STATUS    ROLES     AGE       VERSION
master    Ready     master    1d        v1.11.1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A saída informa que o node &lt;code&gt;master&lt;/code&gt; concluiu todas as tarefas de inicialização e está em um estado &lt;code&gt;Ready&lt;/code&gt; do qual pode começar a aceitar nodes worker e executar tarefas enviadas ao Servidor de API. Agora você pode adicionar os workers a partir de sua máquina local.&lt;/p&gt;

&lt;h2 id="passo-5-—-configurando-os-nodes-worker"&gt;Passo 5 — Configurando os Nodes Worker&lt;/h2&gt;

&lt;p&gt;A adição de workers ao cluster envolve a execução de um único comando em cada um. Este comando inclui as informações necessárias sobre o cluster, tais como o endereço IP e a porta do Servidor de API do master, e um token seguro. Somentes os nodes que passam no token seguro estarão aptos a ingressar no cluster.&lt;/p&gt;

&lt;p&gt;Navegue de volta para a sua área de trabalho e crie um playbook chamado &lt;code&gt;workers.yml&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nano ~/kube-cluster/workers.yml
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Adicione o seguinte texto ao arquivo para adicionar os workers ao cluster:&lt;/p&gt;
&lt;div class="code-label " title="~/kube-cluster/workers.yml"&gt;~/kube-cluster/workers.yml&lt;/div&gt;&lt;pre class="code-pre  local-environment"&gt;&lt;code langs=""&gt;
- hosts: master
  become: yes
  gather_facts: false
  tasks:
    - name: get join command
      shell: kubeadm token create --print-join-command
      register: join_command_raw

    - name: set join command
      set_fact:
        join_command: "{{ join_command_raw.stdout_lines[0] }}"


- hosts: workers
  become: yes
  tasks:
    - name: join cluster
      shell: "{{ hostvars['master'].join_command }} &amp;gt;&amp;gt; node_joined.txt"
      args:
        chdir: $HOME
        creates: node_joined.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Aqui está o que o playbook faz:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;O primeiro play obtém o comando de junção que precisa ser executado nos nodes workers. Este comando estará no seguinte formato: &lt;code&gt;kubeadm join --token &amp;lt;token&amp;gt; &amp;lt;master-ip&amp;gt;:&amp;lt;master-port&amp;gt; --discovery-token-ca-cert-hash sha256:&amp;lt;hash&amp;gt;&lt;/code&gt;. Assim que obtiver o comando real com os valores apropriados de &lt;strong&gt;token&lt;/strong&gt; e &lt;strong&gt;hash&lt;/strong&gt;, a tarefa define isso como um fact para que o próximo play possa acessar essa informação.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;O segundo play tem uma única tarefa que executa o comando de junção em todos os nodes worker. Na conclusão desta tarefa, os dois nodes worker farão parte do cluster.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Salve e feche o arquivo quando você tiver terminado.&lt;/p&gt;

&lt;p&gt;Rode o playbook localmente executando:&lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;ansible-playbook -i hosts ~/kube-cluster/workers.yml
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Na conclusão, você verá uma saída semelhante à seguinte:&lt;/p&gt;
&lt;pre class="code-pre  local-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;PLAY [master] ****

TASK [get join command] ****
changed: [master]

TASK [set join command] *****
ok: [master]

PLAY [workers] *****

TASK [Gathering Facts] *****
ok: [worker1]
ok: [worker2]

TASK [join cluster] *****
changed: [worker1]
changed: [worker2]

PLAY RECAP *****
master                     : ok=2    changed=1    unreachable=0    failed=0   
worker1                    : ok=2    changed=1    unreachable=0    failed=0  
worker2                    : ok=2    changed=1    unreachable=0    failed=0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Com a adição dos nodes worker, seu cluster está agora totalmente configurado e funcional, com os workers prontos para executar cargas de trabalho. Antes de fazer o scheduling de aplicações, vamos verificar se o cluster está funcionando conforme o esperado. &lt;/p&gt;

&lt;h2 id="step-6-—-verificando-o-cluster"&gt;Step 6 — Verificando o Cluster&lt;/h2&gt;

&lt;p&gt;Às vezes, um cluster pode falhar durante a configuração porque um node está inativo ou a conectividade de rede entre o master e o worker não está funcionando corretamente. Vamos verificar o cluster e garantir que os nodes estejam operando corretamente.&lt;/p&gt;

&lt;p&gt;Você precisará verificar o estado atual do cluster a partir do node master para garantir que os nodes estejam prontos. Se você se desconectou do node master, pode voltar e fazer SSH com o seguinte comando:&lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;ssh ubuntu@&lt;span class="highlight"&gt;master_ip&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Em seguida, execute o seguinte comando para obter o status do cluster:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl get nodes
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Você verá uma saída semelhante à seguinte:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;NAME      STATUS    ROLES     AGE       VERSION
master    Ready     master    1d        v1.11.1
worker1   Ready     &amp;lt;none&amp;gt;    1d        v1.11.1 
worker2   Ready     &amp;lt;none&amp;gt;    1d        v1.11.1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Se todos os seus nodes têm o valor &lt;code&gt;Ready&lt;/code&gt; para o &lt;code&gt;STATUS&lt;/code&gt;, significa que eles são parte do cluster e estão prontos para executar cargas de trabalho.&lt;/p&gt;

&lt;p&gt;Se, contudo, alguns dos nodes têm &lt;code&gt;NotReady&lt;/code&gt; como o &lt;code&gt;STATUS&lt;/code&gt;, isso pode significar que os nodes worker ainda não concluíram sua configuração. Aguarde cerca de cinco a dez minutos antes de voltar a executar &lt;code&gt;kubectl get nodes&lt;/code&gt; e fazer a inspeção da nova saída. Se alguns nodes ainda têm &lt;code&gt;NotReady&lt;/code&gt; como status, talvez seja necessário verificar e executar novamente os comandos nas etapas anteriores.  &lt;/p&gt;

&lt;p&gt;Agora que seu cluster foi verificado com sucesso, vamos fazer o scheduling de um exemplo de aplicativo Nginx no cluster.&lt;/p&gt;

&lt;h2 id="step-7-—-executando-uma-aplicação-no-cluster"&gt;Step 7 — Executando Uma Aplicação no Cluster&lt;/h2&gt;

&lt;p&gt;Você pode fazer o deploy de qualquer aplicação containerizada no seu cluster. Para manter as coisas familiares, vamos fazer o deploy do Nginx utilizando &lt;em&gt;Deployments&lt;/em&gt; e &lt;em&gt;Services&lt;/em&gt; para ver como pode ser feito o deploy dessa aplicação no cluster. Você também pode usar os comandos abaixo para outros aplicativos em container, desde que você altere o nome da imagem do Docker e quaisquer flags relevantes (tais como &lt;code&gt;ports&lt;/code&gt; e &lt;code&gt;volumes&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Ainda no node master, execute o seguinte comando para criar um deployment chamado &lt;code&gt;nginx&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl run &lt;span class="highlight"&gt;nginx&lt;/span&gt; --image=&lt;span class="highlight"&gt;nginx&lt;/span&gt; --port &lt;span class="highlight"&gt;80&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Um deployment é um tipo de objeto do Kubernetes que garante que há sempre um número especificado de pods em execução com base em um modelo definido, mesmo se o pod falhar durante o tempo de vida do cluster. O deployment acima irá criar um pod com um container do registro do Docker &lt;a href="https://hub.docker.com/_/nginx/"&gt;Nginx Docker Image&lt;/a&gt;.  &lt;/p&gt;

&lt;p&gt;A seguir, execute o seguinte comando para criar um serviço chamado &lt;code&gt;nginx&lt;/code&gt; que irá expor o app publicamente. Ele fará isso por meio de um &lt;em&gt;NodePort&lt;/em&gt;, um esquema que tornará o pod acessível através de uma porta arbitrária aberta em cada node do cluster:  &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl expose deploy &lt;span class="highlight"&gt;nginx&lt;/span&gt; --port &lt;span class="highlight"&gt;80&lt;/span&gt; --target-port &lt;span class="highlight"&gt;80&lt;/span&gt; --type NodePort
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Services são outro tipo de objeto do Kubernetes que expõe serviços do cluster para os clientes, tanto internos quanto externos. Eles também são capazes de fazer balanceamento de solicitações para vários pods e são um componente integral no Kubernetes, interagindo frequentemente com outros componentes.&lt;/p&gt;

&lt;p&gt;Execute o seguinte comando:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl get services
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Isso produzirá uma saída semelhante à seguinte:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;NAME         TYPE        CLUSTER-IP       EXTERNAL-IP           PORT(S)             AGE
kubernetes   ClusterIP   10.96.0.1        &amp;lt;none&amp;gt;                443/TCP             1d
&lt;span class="highlight"&gt;nginx&lt;/span&gt;        NodePort    10.109.228.209   &amp;lt;none&amp;gt;                80:&lt;span class="highlight"&gt;nginx_port&lt;/span&gt;/TCP   40m
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A partir da terceira linha da saída acima, você pode obter a porta em que o Nginx está sendo executado. O Kubernetes atribuirá uma porta aleatória maior que &lt;code&gt;30000&lt;/code&gt; automaticamente, enquanto garante que a porta já não esteja vinculada a outro serviço.&lt;/p&gt;

&lt;p&gt;Para testar se tudo está funcionando, visite &lt;code&gt;http://&lt;span class="highlight"&gt;worker_1_ip&lt;/span&gt;:&lt;span class="highlight"&gt;nginx_port&lt;/span&gt;&lt;/code&gt; ou &lt;code&gt;http://&lt;span class="highlight"&gt;worker_2_ip&lt;/span&gt;:&lt;span class="highlight"&gt;nginx_port&lt;/span&gt;&lt;/code&gt; através de um navegador na sua máquina local. Você verá a familiar página de boas-vindas do Nginx.&lt;/p&gt;

&lt;p&gt;Se você quiser remover o aplicativo Nginx, primeiro exclua o serviço &lt;code&gt;nginx&lt;/code&gt; do node master:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl delete service &lt;span class="highlight"&gt;nginx&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Execute o seguinte para garantir que o serviço tenha sido excluído:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl get services
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Você verá a seguinte saída:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;[secondary label Output]
NAME         TYPE        CLUSTER-IP       EXTERNAL-IP           PORT(S)        AGE
kubernetes   ClusterIP   10.96.0.1        &amp;lt;none&amp;gt;                443/TCP        1d
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Para excluir o deployment:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl delete deployment &lt;span class="highlight"&gt;nginx&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Execute o seguinte para confirmar que isso funcionou:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl get deployments
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;No resources found.
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="conclusão"&gt;Conclusão&lt;/h2&gt;

&lt;p&gt;Neste guia, você configurou com sucesso um cluster do Kubernetes no Ubuntu 18.04 usando Kubeadm e Ansible para automação.&lt;/p&gt;

&lt;p&gt;Se você está se perguntando o que fazer com o cluster, agora que ele está configurado, um bom próximo passo seria sentir-se confortável para implantar suas próprias aplicações e serviços no cluster. Aqui está uma lista de links com mais informações que podem orientá-lo no processo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://docs.docker.com/engine/examples/"&gt;Dockerizing applications&lt;/a&gt; - lista exemplos que detalham como containerizar aplicações usando o Docker.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://kubernetes.io/docs/concepts/workloads/pods/pod-overview/"&gt;Pod Overview&lt;/a&gt; - descreve em detalhes como os Pods funcionam e seu relacionamento com outros objetos do Kubernetes. Os pods são onipresentes no Kubernetes, então compreendê-los facilitará seu trabalho.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://kubernetes.io/docs/concepts/workloads/controllers/deployment/"&gt;Deployments Overview&lt;/a&gt; - fornece uma visão geral dos deployments. É útil entender como os controladores, como os deployments, funcionam, pois eles são usados com frequência em aplicações stateless para escalonamento e na recuperação automatizada de aplicações não íntegras.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://kubernetes.io/docs/concepts/services-networking/service/"&gt;Services Overview&lt;/a&gt; - cobre os serviços ou services, outro objeto frequentemente usado em clusters do Kubernetes. Entender os tipos de serviços e as opções que eles têm é essencial para executar aplicações stateless e stateful.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Outros conceitos importantes que você pode analisar são &lt;a href="https://kubernetes.io/docs/concepts/storage/volumes/"&gt;Volumes&lt;/a&gt;, &lt;a href="https://kubernetes.io/docs/concepts/services-networking/ingress/"&gt;Ingresses&lt;/a&gt; e &lt;a href="https://kubernetes.io/docs/concepts/configuration/secret/"&gt;Secrets&lt;/a&gt;, os quais são úteis ao realizar o deploy de aplicações em produção. &lt;/p&gt;

&lt;p&gt;O Kubernetes tem muitas funcionalidades e recursos a oferecer. &lt;a href="https://kubernetes.io/docs/"&gt;A Documentação Oficial do Kubernetes&lt;/a&gt; é o melhor lugar para aprender sobre conceitos, encontrar guias específicos de tarefas e procurar referências de API para vários objetos.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Por bsder&lt;/em&gt;&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-install-webmin-on-debian-9</id>
    <published>2018-09-07T21:44:48Z</published>
    <updated>2018-09-07T22:44:42Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-install-webmin-on-debian-9"/>
    <title>How To Install Webmin on Debian 9</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;&lt;a href="http://www.webmin.com/"&gt;Webmin&lt;/a&gt; is a modern web control panel for any Linux machine that allows you to administer your server through a simple interface.  With Webmin, you can change settings for common packages on the fly.&lt;/p&gt;

&lt;p&gt;In this tutorial, you'll install and configure Webmin on your server and secure access to the interface with a valid certificate using &lt;a href="https://letsencrypt.org/"&gt;Let's Encrypt&lt;/a&gt;. You'll then use Webmin to add new user accounts, and update all packages on your server from the dashboard.&lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;To complete this tutorial, you will need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One Debian 9 server set up by following &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-debian-9"&gt;the Debian 9 initial server setup guide&lt;/a&gt;, including a sudo non-root user and a firewall.&lt;/li&gt;
&lt;li&gt;Apache installed by following Step 1 of &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-linux-apache-mariadb-php-lamp-stack-debian9#step-1-%E2%80%94-installing-apache-and-updating-the-firewall"&gt;How To Install Linux, Apache, MariaDB, PHP (LAMP) stack on Debian 9&lt;/a&gt;. We'll use Apache to perform Let's Encrypt's domain verification.&lt;/li&gt;
&lt;li&gt;A Fully-Qualified Domain Name (FQDN), with a DNS &lt;strong&gt;A&lt;/strong&gt; record pointing to the IP address of your server. To configure this, follow &lt;a href="https://www.digitalocean.com/docs/networking/dns/"&gt;these instructions on DNS hosting on DigitalOcean&lt;/a&gt;. &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="step-1-—-installing-webmin"&gt;Step 1 — Installing Webmin&lt;/h2&gt;

&lt;p&gt;First, we need to add the Webmin repository so that we can easily install and update Webmin using our package manager. We do this by adding the repository to the &lt;code&gt;/etc/apt/sources.list&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Open the file in your editor:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/apt/sources.list
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then add this line to the bottom of the file to add the new repository:&lt;/p&gt;
&lt;div class="code-label " title="/etc/apt/sources.list"&gt;/etc/apt/sources.list&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt; . . . 
&lt;span class="highlight"&gt;deb http://download.webmin.com/download/repository sarge contrib&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save the file and exit the editor.&lt;/p&gt;

&lt;p&gt;Next, add the Webmin PGP key so that your system will trust the new repository:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;wget http://www.webmin.com/jcameron-key.asc
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo apt-key add jcameron-key.asc
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, update the list of packages to include the Webmin repository:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt update 
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then install Webmin:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install webmin 
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once the installation finishes, you be presented with the following output:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Webmin install complete. You can now login to 
https://&lt;span class="highlight"&gt;your_server_ip&lt;/span&gt;:10000 as root with your 
root password, or as any user who can use `sudo`.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Please copy down this information, as you will need it for the next step.&lt;/p&gt;

&lt;p&gt;&lt;span class='note'&gt;&lt;strong&gt;Note:&lt;/strong&gt; If you installed &lt;code&gt;ufw&lt;/code&gt; during the prerequisite step, you will need to run the command &lt;code&gt;sudo ufw allow 10000&lt;/code&gt; in order to allow Webmin through the firewall. For extra security, you may want to configure your firewall to only allow access to this port from certain IP ranges.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;Let's secure access to Webmin by adding a valid certificate.&lt;/p&gt;

&lt;h2 id="step-2-—-adding-a-valid-certificate-with-let-39-s-encrypt"&gt;Step 2 — Adding a Valid Certificate with Let's Encrypt&lt;/h2&gt;

&lt;p&gt;Webmin is already configured to use HTTPS, but it uses a self-signed, untrusted certificate. Let's replace it with a valid certificate from Let's Encrypt. &lt;/p&gt;

&lt;p&gt;Navigate to &lt;code&gt;https://&lt;span class="highlight"&gt;your_domain&lt;/span&gt;:10000&lt;/code&gt; in your web browser, replacing &lt;code&gt;&lt;span class="highlight"&gt;your_domain&lt;/span&gt;&lt;/code&gt; with the domain name you pointed at your server. &lt;/p&gt;

&lt;p&gt;&lt;span class='note'&gt;&lt;strong&gt;Note:&lt;/strong&gt; When logging in for the first time, you will see an "Invalid SSL" error. This is because the server has generated a self-signed certificate.  Allow the exception to continue so you can replace the self-signed certificate with one from Let's Encrypt.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;You'll be presented with a login screen. Sign in with the non-root user you created while fulfilling the prerequisites for this tutorial.&lt;/p&gt;

&lt;p&gt;Once you log in, the first screen you will see is the Webmin dashboard. Before you can apply a valid certificate, you have to set the server's hostname. Look for the &lt;strong&gt;System hostname&lt;/strong&gt; field and click on the link to the right, as shown in the following figure:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/webmin_ubuntu1604/ihomuI4.png" alt="Image showing where the link is on the Webmin dashboard"&gt;&lt;/p&gt;

&lt;p&gt;This wil take you to the &lt;strong&gt;Hostname and DNS Client&lt;/strong&gt; page. Locate the &lt;strong&gt;Hostname&lt;/strong&gt; field, and enter your Fully-Qualified Domain Name into the field. Then press the &lt;strong&gt;Save&lt;/strong&gt; button at the bottom of the page to apply the setting.&lt;/p&gt;

&lt;p&gt;After you've set your hostname, click on &lt;strong&gt;Webmin&lt;/strong&gt; on the left navigation bar, and then click on &lt;strong&gt;Webmin Configuration&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Then, select &lt;strong&gt;SSL Encryption&lt;/strong&gt; from the list of icons, and then select the &lt;strong&gt;Let's Encrypt&lt;/strong&gt; tab. You'll see a screen like the following figure:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/webmin_debian_9/webmin_domain.png" alt="Image showing the Let's Encrypt tab of the SSL Encryption section"&gt;&lt;/p&gt;

&lt;p&gt;Using this screen, you'll tell Webmin how to obtain and renew your certificate. Let's Encrypt certificates expire after 3 months, but we can instruct Webmin to automatically attempt to renew the Let's Encrypt certificate every month. Let's Encrypt looks for a verification file on our server, so we'll configure Webmin to place the verification file inside the folder &lt;code&gt;/var/www/html&lt;/code&gt;, which is the folder that the Apache web server you configured in the prerequisites uses. Follow these steps to set up your certificate:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Fill in &lt;strong&gt;Hostnames for certificate&lt;/strong&gt; with your FQDN.&lt;/li&gt;
&lt;li&gt;For &lt;strong&gt;Website root directory for validation file&lt;/strong&gt;, select the &lt;strong&gt;Other Directory&lt;/strong&gt; button and enter &lt;code&gt;/var/www/html&lt;/code&gt;. &lt;/li&gt;
&lt;li&gt;For &lt;strong&gt;Months between automatic renewal&lt;/strong&gt; section, deselect the &lt;strong&gt;Only renew manually&lt;/strong&gt; option by typing &lt;code&gt;1&lt;/code&gt; into the input box, and selecting the radio button to the left of the input box.&lt;/li&gt;
&lt;li&gt;Click the &lt;strong&gt;Request Certificate&lt;/strong&gt; button. After a few seconds, you will see a confirmation screen.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To use the new certificate, restart Webmin by clicking the back arrow in your browser, and clicking the &lt;strong&gt;Restart Webmin&lt;/strong&gt; button. Wait around 30 seconds, and then reload the page and log in again. Your browser should now indicate that the certificate is valid.&lt;/p&gt;

&lt;h2 id="step-3-–-using-webmin"&gt;Step 3 – Using Webmin&lt;/h2&gt;

&lt;p&gt;You've now set up a secured working instance of Webmin. Let's look at how to use it.&lt;/p&gt;

&lt;p&gt;Webmin has many different modules that can control everything from the BIND DNS Server to something as simple as adding users to the system.  Let's look at how to create a new user, and then explore how to update the operating system using Webmin.&lt;/p&gt;

&lt;h3 id="managing-users-and-groups"&gt;Managing Users and Groups&lt;/h3&gt;

&lt;p&gt;Let's explore how  to manage the users and groups on your server. &lt;/p&gt;

&lt;p&gt;First, click the &lt;strong&gt;System&lt;/strong&gt; tab, and then click the &lt;strong&gt;Users and Groups&lt;/strong&gt; button. Then, from here, you can either add a user, manage a user, or add or manage a group.&lt;/p&gt;

&lt;p&gt;Let's create a new user called &lt;strong&gt;deploy&lt;/strong&gt; which can be used for hosting web applications. To add a user, click &lt;strong&gt;Create a new user&lt;/strong&gt;, which is located at the top of the users table. This displays the &lt;strong&gt;Create User&lt;/strong&gt; screen, where you can supply the username, password, groups and other options. Follow these instructions to create the user:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Fill in &lt;strong&gt;Username&lt;/strong&gt; with &lt;code&gt;deploy&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;Automatic&lt;/strong&gt; for &lt;strong&gt;User ID&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Fill in &lt;strong&gt;Real Name&lt;/strong&gt; with a descriptive name like &lt;code&gt;Deployment user&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;For &lt;strong&gt;Home Directory&lt;/strong&gt;, select &lt;strong&gt;Automatic&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;For &lt;strong&gt;Shell&lt;/strong&gt;, select &lt;strong&gt;/bin/bash&lt;/strong&gt; from the dropdown list.&lt;/li&gt;
&lt;li&gt;For &lt;strong&gt;Password&lt;/strong&gt;, select &lt;strong&gt;Normal Password&lt;/strong&gt; and type in a password of your choice.&lt;/li&gt;
&lt;li&gt;For &lt;strong&gt;Primary Group&lt;/strong&gt;, select &lt;strong&gt;New group with same name as user&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;For &lt;strong&gt;Secondary Group&lt;/strong&gt;, select &lt;strong&gt;sudo&lt;/strong&gt; from the &lt;strong&gt;All groups&lt;/strong&gt; list, and press the &lt;strong&gt;-&amp;gt;&lt;/strong&gt; button to add the group to the &lt;strong&gt;in groups&lt;/strong&gt; list.&lt;/li&gt;
&lt;li&gt;Press &lt;strong&gt;Create&lt;/strong&gt; to create this new user.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When creating a user, you can set options for password expiry, the user's shell, and whether or not they are allowed a home directory. &lt;/p&gt;

&lt;p&gt;Next, let's look at how to install updates to our system.&lt;/p&gt;

&lt;h3 id="updating-packages"&gt;Updating Packages&lt;/h3&gt;

&lt;p&gt;Webmin lets you update all of your packages through its user interface. To update all of your packages, first, go to the &lt;strong&gt;Dashboard&lt;/strong&gt; link, and then locate the &lt;strong&gt;Package updates&lt;/strong&gt; field. If there are updates available, you'll see a link that states the number of available updates, as shown in the following figure:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/webmin_debian_9/webmin_package_updates_two.png" alt="Webmin shows the number of updates available"&gt;&lt;/p&gt;

&lt;p&gt;Click this link, and then press &lt;strong&gt;Update selected packages&lt;/strong&gt; to start the update.  You may be asked to reboot the server, which you can also do through the Webmin interface.&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;You now have a secured working instance of Webmin and you've used the interface to create a user and update packages. Webmin gives you access to many things you'd normally need to access through the console, and it organizes them in an intuitive way. For example, if you have Apache installed, you would find the configuration tab for it under &lt;strong&gt;Servers&lt;/strong&gt;, and then &lt;strong&gt;Apache&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Explore the interface, or read the &lt;a href="http://doxfer.webmin.com/Webmin/Main_Page"&gt;Official Webmin wiki&lt;/a&gt; to learn more about managing your system with Webmin.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-install-git-on-debian-9</id>
    <published>2018-09-07T22:40:33Z</published>
    <updated>2018-09-07T22:40:57Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-install-git-on-debian-9"/>
    <title>How To Install Git on Debian 9</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;Software version control systems enable you to keep track of your software at the source level. With versioning tools, you can track changes, revert to previous stages, and branch to create alternate versions of files and directories.&lt;/p&gt;

&lt;p&gt;Git is one of the most popular version control systems currently available. Many projects’ files are maintained in a Git repository, and sites like GitHub, GitLab, and Bitbucket help to facilitate software development project sharing and collaboration.&lt;/p&gt;

&lt;p&gt;In this tutorial, we’ll install and configure Git on a Debian 9 server. We will cover how to install the software in two different ways, each of which have their own benefits depending on your specific needs.&lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;In order to complete this tutorial, you should have a non-root user with &lt;code&gt;sudo&lt;/code&gt; privileges on an Debian 9 server. To learn how to achieve this setup, follow our &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-debian-9"&gt;Debian 9 initial server setup guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With your server and user set up, you are ready to begin.&lt;/p&gt;

&lt;h2 id="installing-git-with-default-packages"&gt;Installing Git with Default Packages&lt;/h2&gt;

&lt;p&gt;Debian’s default repositories provide you with a fast method to install Git. Note that the version you install via these repositories may be older than the newest version currently available. If you need the latest release, consider moving to the &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-git-on-debian-9#installing-git-from-source"&gt;next section&lt;/a&gt; of this tutorial to learn how to install and compile Git from source.&lt;/p&gt;

&lt;p&gt;First, use the apt package management tools to update your local package index. With the update complete, you can download and install Git:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt update
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo apt install git
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can confirm that you have installed Git correctly by running the following command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;git --version
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;git version &lt;span class="highlight"&gt;2.11.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With Git successfully installed, you can now move on to the &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-git-on-debian-9#setting-up-git"&gt;Setting Up Git&lt;/a&gt; section of this tutorial to complete your setup.&lt;/p&gt;

&lt;h2 id="installing-git-from-source"&gt;Installing Git from Source&lt;/h2&gt;

&lt;p&gt;A more flexible method of installing Git is to compile the software from source. This takes longer and will not be maintained through your package manager, but it will allow you to download the latest release and will give you some control over the options you include if you wish to customize.&lt;/p&gt;

&lt;p&gt;Before you begin, you need to install the software that Git depends on. This is all available in the default repositories, so we can update our local package index and then install the packages.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt update
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo apt install make libssl-dev libghc-zlib-dev libcurl4-gnutls-dev libexpat1-dev gettext unzip
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After you have installed the necessary dependencies, you can go ahead and get the version of Git you want by visiting the &lt;a href="https://github.com/git/git"&gt;Git project’s mirror on GitHub&lt;/a&gt;, available via the following URL:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;https://github.com/git/git
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;From here, be sure that you are on the &lt;code&gt;master&lt;/code&gt; branch. Click on the &lt;strong&gt;Tags&lt;/strong&gt; link and select your desired Git version. Unless you have a reason for downloading a &lt;em&gt;release candidate&lt;/em&gt; (marked as &lt;strong&gt;rc&lt;/strong&gt;) version, try to avoid these as they may be unstable. &lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/git_install_1604/branch-tags.png" alt="git change branch select tags"&gt;&lt;/p&gt;

&lt;p&gt;Next, on the right side of the page, click on the &lt;strong&gt;Clone or download&lt;/strong&gt; button, then right-click on &lt;strong&gt;Download ZIP&lt;/strong&gt; and copy the link address that ends in &lt;code&gt;.zip&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/git_install_1604/download-zip.png" alt="right-click on download zip to copy url"&gt;&lt;/p&gt;

&lt;p&gt;Back on your Debian 9 server, move into the &lt;code&gt;tmp&lt;/code&gt; directory to download temporary files. &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cd /tmp
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;From there, you can use the &lt;code&gt;wget&lt;/code&gt; command to install the copied zip file link. We’ll specify a new name for the file: &lt;code&gt;git.zip&lt;/code&gt;.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;wget https://github.com/git/git/archive/&lt;span class="highlight"&gt;v2.18.0&lt;/span&gt;.zip -O git.zip
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Unzip the file that you downloaded and move into the resulting directory by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;unzip git.zip
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;cd git-*
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, you can make the package and install it by typing these two commands:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;make prefix=/usr/local all
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo make prefix=/usr/local install
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To ensure that the install was successful, you can type &lt;code&gt;git --version&lt;/code&gt; and you should receive relevant output that specifies the current installed version of Git.&lt;/p&gt;

&lt;p&gt;Now that you have Git installed, if you want to upgrade to a later version, you can clone the repository, and then build and install. To find the URL to use for the clone operation, navigate to the branch or tag that you want on the &lt;a href="https://github.com/git/git"&gt;project's GitHub page&lt;/a&gt; and then copy the clone URL on the right side:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/git_install_1604/copy-url.png" alt="git copy URL"&gt;&lt;/p&gt;

&lt;p&gt;At the time of writing, the relevant URL is:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;https://github.com/git/git.git
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Change to your home directory, and use &lt;code&gt;git clone&lt;/code&gt; on the URL you just copied:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cd ~
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;git clone &lt;span class="highlight"&gt;https://github.com/git/git.git&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will create a new directory within your current directory where you can rebuild the package and reinstall the newer version, just like you did above. This will overwrite your older version with the new version:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cd git
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;make prefix=/usr/local all
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo make prefix=/usr/local install
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With this complete, you can be sure that your version of Git is up to date.&lt;/p&gt;

&lt;h2 id="setting-up-git"&gt;Setting Up Git&lt;/h2&gt;

&lt;p&gt;Now that you have Git installed, you should configure it so that the generated commit messages will contain your correct information.&lt;/p&gt;

&lt;p&gt;This can be achieved by using the &lt;code&gt;git config&lt;/code&gt; command. Specifically, we need to provide our name and email address because Git embeds this information into each commit we do. We can go ahead and add this information by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;git config --global user.name "&lt;span class="highlight"&gt;Sammy&lt;/span&gt;"
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;git config --global user.email "&lt;span class="highlight"&gt;sammy@domain.com&lt;/span&gt;"
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can see all of the configuration items that have been set by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;git config --list
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;user.name=&lt;span class="highlight"&gt;Sammy&lt;/span&gt;
user.email=&lt;span class="highlight"&gt;sammy@domain.com&lt;/span&gt;
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The information you enter is stored in your Git configuration file, which you can optionally edit by hand with a text editor like this:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nano ~/.gitconfig
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class="code-label " title="~/.gitconfig contents"&gt;~/.gitconfig contents&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;[user]
  name = &lt;span class="highlight"&gt;Sammy&lt;/span&gt;
  email = &lt;span class="highlight"&gt;sammy@domain.com&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are many other options that you can set, but these are the two essential ones needed. If you skip this step, you’ll likely see warnings when you commit to Git. This makes more work for you because you will then have to revise the commits you have done with the corrected information.&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;You should now have Git installed and ready to use on your system. &lt;/p&gt;

&lt;p&gt;To learn more about how to use Git, check out these articles and series:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.digitalocean.com/community/articles/how-to-use-git-effectively"&gt;How To Use Git Effectively&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.digitalocean.com/community/articles/how-to-use-git-branches"&gt;How To Use Git Branches&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.digitalocean.com/community/tutorial_series/an-introduction-to-open-source"&gt;An Introduction to Open Source series&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-set-up-jupyter-notebook-with-python-3-on-debian-9</id>
    <published>2018-09-07T22:11:01Z</published>
    <updated>2018-09-07T22:37:32Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-set-up-jupyter-notebook-with-python-3-on-debian-9"/>
    <title>How To Set Up a Jupyter Notebook with Python 3 on Debian 9</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;&lt;a href="http://jupyter.org/"&gt;Jupyter Notebook&lt;/a&gt; offers a command shell for interactive computing as a web application. The tool can be used with several languages, including Python, Julia, R, Haskell, and Ruby. It is often used for working with data, statistical modeling, and machine learning.&lt;/p&gt;

&lt;p&gt;This tutorial will walk you through setting up Jupyter Notebook to run from a Debian 9 server, as well as teach you how to connect to and use the notebook. Jupyter notebooks (or simply notebooks) are documents produced by the Jupyter Notebook app which contain both computer code and rich text elements (paragraph, equations, figures, links, etc.) which aid in presenting and sharing reproducible research. &lt;/p&gt;

&lt;p&gt;By the end of this guide, you will be able to run Python 3 code using Jupyter Notebook running on a remote server. &lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;In order to complete this guide, you should have a fresh Debian 9 server instance with a basic firewall and a non-root user with sudo privileges configured. You can learn how to set this up by running through our &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-debian-9"&gt;Initial Server Setup with Debian 9&lt;/a&gt; guide.&lt;/p&gt;

&lt;h2 id="step-1-—-install-pip-and-python-headers"&gt;Step 1 — Install Pip and Python Headers&lt;/h2&gt;

&lt;p&gt;To begin the process, we'll download and install all of the items we need from the Debian repositories.  We will use the Python package manager &lt;code&gt;pip&lt;/code&gt; to install additional components a bit later.&lt;/p&gt;

&lt;p&gt;We first need to update the local &lt;code&gt;apt&lt;/code&gt; package index and then download and install the packages:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt update
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, install &lt;code&gt;pip&lt;/code&gt; and the Python header files, which are used by some of Jupyter’s dependencies:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install python3-pip python3-dev
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Debian 9 (“Stretch”) comes preinstalled with Python 3.5.&lt;/p&gt;

&lt;p&gt;We can now move on to setting up a Python virtual environment into which we’ll install Jupyter.&lt;/p&gt;

&lt;h2 id="step-2-—-create-a-python-virtual-environment-for-jupyter"&gt;Step 2 — Create a Python Virtual Environment for Jupyter&lt;/h2&gt;

&lt;p&gt;Now that we have Python 3, its header files, and &lt;code&gt;pip&lt;/code&gt; ready to go, we can create a Python virtual environment for easier management. We will install Jupyter into this virtual environment.&lt;/p&gt;

&lt;p&gt;To do this, we first need access to the &lt;code&gt;virtualenv&lt;/code&gt; command.  We can install this with &lt;code&gt;pip&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Upgrade &lt;code&gt;pip&lt;/code&gt; and install the package by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo -H pip3 install --upgrade pip
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo -H pip3 install virtualenv
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With &lt;code&gt;virtualenv&lt;/code&gt; installed, we can start forming our environment. Create and move into a directory where we can keep our project files:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;mkdir ~/&lt;span class="highlight"&gt;myprojectdir&lt;/span&gt;
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;cd ~/&lt;span class="highlight"&gt;myprojectdir&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Within the project directory, create a Python virtual environment by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;virtualenv &lt;span class="highlight"&gt;myprojectenv&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will create a directory called &lt;code&gt;&lt;span class="highlight"&gt;myprojectenv&lt;/span&gt;&lt;/code&gt; within your &lt;code&gt;&lt;span class="highlight"&gt;myprojectdir&lt;/span&gt;&lt;/code&gt; directory.  Inside, it will install a local version of Python and a local version of &lt;code&gt;pip&lt;/code&gt;.  We can use this to install and configure an isolated Python environment for Jupyter.&lt;/p&gt;

&lt;p&gt;Before we install Jupyter, we need to activate the virtual environment.  You can do that by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;source &lt;span class="highlight"&gt;myprojectenv&lt;/span&gt;/bin/activate
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Your prompt should change to indicate that you are now operating within a Python virtual environment.  It will look something like this: &lt;code&gt;(&lt;span class="highlight"&gt;myprojectenv&lt;/span&gt;)&lt;span class="highlight"&gt;user&lt;/span&gt;@&lt;span class="highlight"&gt;host&lt;/span&gt;:~/&lt;span class="highlight"&gt;myprojectdir&lt;/span&gt;$&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You’re now ready to install Jupyter into this virtual environment.&lt;/p&gt;

&lt;h2 id="step-3-—-install-jupyter"&gt;Step 3 — Install Jupyter&lt;/h2&gt;

&lt;p&gt;With your virtual environment active, install Jupyter with the local instance of &lt;code&gt;pip&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;span class='note'&gt;&lt;strong&gt;Note:&lt;/strong&gt; When the virtual environment is activated (when your prompt has &lt;code&gt;(myprojectenv)&lt;/code&gt; preceding it), use &lt;code&gt;pip&lt;/code&gt; instead of &lt;code&gt;pip3&lt;/code&gt;, even if you are using Python 3.  The virtual environment's copy of the tool is always named &lt;code&gt;pip&lt;/code&gt;, regardless of the Python version.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;pip install jupyter
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At this point, you’ve successfully installed all the software needed to run Jupyter. We can now start the notebook server.&lt;/p&gt;

&lt;h2 id="step-4-—-run-jupyter-notebook"&gt;Step 4 — Run Jupyter Notebook&lt;/h2&gt;

&lt;p&gt;You now have everything you need to run Jupyter Notebook! To run it, execute the following command:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="(myprojectenv)sammy@your_server:~/myprojectdir$"&gt;jupyter notebook
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A log of the activities of the Jupyter Notebook will be printed to the terminal.  When you run Jupyter Notebook, it runs on a specific port number. The first notebook you run will usually use port &lt;code&gt;&lt;span class="highlight"&gt;8888&lt;/span&gt;&lt;/code&gt;.  To check the specific port number Jupyter Notebook is running on, refer to the output of the command used to start it:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;[I 21:23:21.198 NotebookApp] Writing notebook server cookie secret to /run/user/1001/jupyter/notebook_cookie_secret
[I 21:23:21.361 NotebookApp] Serving notebooks from local directory: /home/&lt;span class="highlight"&gt;sammy&lt;/span&gt;/&lt;span class="highlight"&gt;myprojectdir&lt;/span&gt;
[I 21:23:21.361 NotebookApp] The Jupyter Notebook is running at:
[I 21:23:21.361 NotebookApp] http://localhost:&lt;span class="highlight"&gt;8888&lt;/span&gt;/?token=&lt;span class="highlight"&gt;1fefa6ab49a498a3f37c959404f7baf16b9a2eda3eaa6d72&lt;/span&gt;
[I 21:23:21.361 NotebookApp] Use Control-C to stop this server and shut down all kernels (twice to skip confirmation).
[W 21:23:21.361 NotebookApp] No web browser found: could not locate runnable browser.
[C 21:23:21.361 NotebookApp]

    Copy/paste this URL into your browser when you connect for the first time,
    to login with a token:
        http://localhost:&lt;span class="highlight"&gt;8888&lt;/span&gt;/?token=&lt;span class="highlight"&gt;1fefa6ab49a498a3f37c959404f7baf16b9a2eda3eaa6d72&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you are running Jupyter Notebook on a local Debian computer (not on a Droplet), you can simply navigate to the displayed URL to connect to Jupyter Notebook.  If you are running Jupyter Notebook on a Droplet, you will need to connect to the server using SSH tunneling as outlined in the next section.&lt;/p&gt;

&lt;p&gt;At this point, you can keep the SSH connection open and keep Jupyter Notebook running or can exit the app and re-run it once you set up SSH tunneling.  Let's keep it simple and stop the Jupyter Notebook process. We will run it again once we have SSH tunneling working.  To stop the Jupyter Notebook process, press &lt;code&gt;CTRL+C&lt;/code&gt;, type &lt;code&gt;Y&lt;/code&gt;, and hit &lt;code&gt;ENTER&lt;/code&gt; to confirm.  The following will be displayed:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;[C 21:28:28.512 NotebookApp] Shutdown confirmed
[I 21:28:28.512 NotebookApp] Shutting down 0 kernels
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We’ll now set up an SSH tunnel so that we can access the notebook.&lt;/p&gt;

&lt;h2 id="step-5-—-connect-to-the-server-using-ssh-tunneling"&gt;Step 5 — Connect to the Server Using SSH Tunneling&lt;/h2&gt;

&lt;p&gt;In this section we will learn how to connect to the Jupyter Notebook web interface using SSH tunneling. Since Jupyter Notebook will run on a specific port on the server (such as &lt;code&gt;:8888&lt;/code&gt;, &lt;code&gt;:8889&lt;/code&gt; etc.), SSH tunneling enables you to connect to the server’s port securely.&lt;/p&gt;

&lt;p&gt;The next two subsections describe how to create an SSH tunnel from 1) a Mac or Linux and 2) Windows. Please refer to the subsection for your local computer.&lt;/p&gt;

&lt;h3 id="ssh-tunneling-with-a-mac-or-linux"&gt;SSH Tunneling with a Mac or Linux&lt;/h3&gt;

&lt;p&gt;If you are using a Mac or Linux, the steps for creating an SSH tunnel are similar to using SSH to log in to your remote server, except that there are additional parameters in the &lt;code&gt;ssh&lt;/code&gt; command.  This subsection will outline the additional parameters needed in the &lt;code&gt;ssh&lt;/code&gt; command to tunnel successfully. &lt;/p&gt;

&lt;p&gt;SSH tunneling can be done by running the following SSH command in a new local terminal window:&lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;ssh -L &lt;span class="highlight"&gt;8888&lt;/span&gt;:localhost:&lt;span class="highlight"&gt;8888&lt;/span&gt; &lt;span class="highlight"&gt;your_server_username&lt;/span&gt;@&lt;span class="highlight"&gt;your_server_ip&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;ssh&lt;/code&gt; command opens an SSH connection, but &lt;code&gt;-L&lt;/code&gt; specifies that the given port on the local (client) host is to be forwarded to the given host and port on the remote side (server). This means that whatever is running on the second port number (e.g. &lt;code&gt;&lt;span class="highlight"&gt;8888&lt;/span&gt;&lt;/code&gt;) on the server will appear on the first port number (e.g. &lt;code&gt;&lt;span class="highlight"&gt;8888&lt;/span&gt;&lt;/code&gt;) on your local computer.  &lt;/p&gt;

&lt;p&gt;Optionally change port &lt;code&gt;&lt;span class="highlight"&gt;8888&lt;/span&gt;&lt;/code&gt; to one of your choosing to avoid using a port already in use by another process. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;span class="highlight"&gt;server_username&lt;/span&gt;&lt;/code&gt; is your username (e.g. &lt;span class="highlight"&gt;sammy&lt;/span&gt;) on the server which you created and &lt;code&gt;&lt;span class="highlight"&gt;your_server_ip&lt;/span&gt;&lt;/code&gt; is the  IP address of your server.  &lt;/p&gt;

&lt;p&gt;For example, for the username &lt;code&gt;&lt;span class="highlight"&gt;sammy&lt;/span&gt;&lt;/code&gt; and the server address &lt;code&gt;&lt;span class="highlight"&gt;203.0.113.0&lt;/span&gt;&lt;/code&gt;, the command would be:&lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;ssh -L &lt;span class="highlight"&gt;8888&lt;/span&gt;:localhost:&lt;span class="highlight"&gt;8888&lt;/span&gt; &lt;span class="highlight"&gt;sammy&lt;/span&gt;@&lt;span class="highlight"&gt;203.0.113.0&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If no error shows up after running the &lt;code&gt;ssh -L&lt;/code&gt; command, you can move into your programming environment and run Jupyter Notebook:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="(myprojectenv)sammy@your_server:~/myprojectdir$"&gt;jupyter notebook
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You’ll receive output with a URL. From a web browser on your local machine, open the Jupyter Notebook web interface with the URL that starts with &lt;code&gt;http://localhost:8888&lt;/code&gt;. Ensure that the token number is included, or enter the token number string when prompted at &lt;code&gt;http://localhost:8888&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id="ssh-tunneling-with-windows-and-putty"&gt;SSH Tunneling with Windows and Putty&lt;/h3&gt;

&lt;p&gt;If you are using Windows, you can create an SSH tunnel using &lt;a href="https://www.putty.org/"&gt;Putty&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;First, enter the server URL or IP address as the hostname as shown:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/jupyter_notebook/set_hostname_putty.png" alt="Set Hostname for SSH Tunnel"&gt;&lt;/p&gt;

&lt;p&gt;Next, click &lt;strong&gt;SSH&lt;/strong&gt; on the bottom of the left pane to expand the menu, and then click &lt;strong&gt;Tunnels&lt;/strong&gt;.  Enter the local port number to use to access Jupyter on your local machine.  Choose  &lt;code&gt;&lt;span class="highlight"&gt;8000&lt;/span&gt;&lt;/code&gt; or greater to avoid ports used by other services, and set the destination as &lt;code&gt;localhost:&lt;span class="highlight"&gt;8888&lt;/span&gt;&lt;/code&gt; where &lt;code&gt;&lt;span class="highlight"&gt;:8888&lt;/span&gt;&lt;/code&gt; is the number of the port that Jupyter Notebook is running on.  &lt;/p&gt;

&lt;p&gt;Now click the &lt;strong&gt;Add&lt;/strong&gt; button, and the ports should appear in the &lt;strong&gt;Forwarded ports&lt;/strong&gt; list:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/jupyter_notebook/forwarded_ports_putty.png" alt="Forwarded ports list"&gt;&lt;/p&gt;

&lt;p&gt;Finally, click the &lt;strong&gt;Open&lt;/strong&gt; button to connect to the server via SSH and tunnel the desired ports.  Navigate to &lt;code&gt;http://localhost:&lt;span class="highlight"&gt;8000&lt;/span&gt;&lt;/code&gt; (or whatever port you chose) in a web browser to connect to Jupyter Notebook running on the server. Ensure that the token number is included, or enter the token number string when prompted at &lt;code&gt;http://localhost:8000&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id="step-6-—-using-jupyter-notebook"&gt;Step 6 — Using Jupyter Notebook&lt;/h2&gt;

&lt;p&gt;This section goes over the basics of using Jupyter Notebook. If you don’t currently have Jupyter Notebook running, start it with the &lt;code&gt;jupyter notebook&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;You should now be connected to it using a web browser. Jupyter Notebook is very powerful and has many features. This section will outline a few of the basic features to get you started using the notebook. Jupyter Notebook will show all of the files and folders in the directory it is run from, so when you’re working on a project make sure to start it from the project directory.&lt;/p&gt;

&lt;p&gt;To create a new notebook file, select &lt;strong&gt;New&lt;/strong&gt; &amp;gt; &lt;strong&gt;Python 3&lt;/strong&gt; from the top right pull-down menu:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/eng_python/JupyterNotebookPy3/jupyter-notebook-new.png" alt="Create a new Python 3 notebook"&gt;&lt;/p&gt;

&lt;p&gt;This will open a notebook. We can now run Python code in the cell or change the cell to markdown.  For example, change the first cell to accept Markdown by clicking &lt;strong&gt;Cell&lt;/strong&gt; &amp;gt; &lt;strong&gt;Cell Type&lt;/strong&gt; &amp;gt; &lt;strong&gt;Markdown&lt;/strong&gt; from the top navigation bar.  We can now write notes using Markdown and even include equations written in &lt;a href="https://www.latex-project.org/"&gt;LaTeX&lt;/a&gt; by putting them between the &lt;code&gt;$$&lt;/code&gt; symbols.  For example, type the following into the cell after changing it to markdown:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;# Simple Equation

Let us now implement the following equation:
$$ y = x^2$$

where $x = 2$
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To turn the markdown into rich text, press &lt;code&gt;CTRL+ENTER&lt;/code&gt;, and the following should be the results:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/jupyter_notebook/markdown_results.png" alt="results of markdown"&gt;&lt;/p&gt;

&lt;p&gt;You can use the markdown cells to make notes and document your code.  Let's implement that simple equation and print the result. Click on the top cell, then press &lt;code&gt;ALT+ENTER&lt;/code&gt; to add a cell below it. Enter the following code in the new cell.&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-python"&gt;x = 2
y = x**2
print(y)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To run the code, press &lt;code&gt;CTRL+ENTER&lt;/code&gt;. You’ll receive the following results:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/eng_python/JupyterNotebookPy3/jupyter-notebook-md-python.png" alt="simple equation results"&gt;&lt;/p&gt;

&lt;p&gt;You now have the ability to &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-import-modules-in-python-3"&gt;import modules&lt;/a&gt; and use the notebook as you would with any other Python development environment!&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Congratulations! You should now be able to write reproducible Python code and notes in Markdown using Jupyter Notebook. To get a quick tour of Jupyter Notebook from within the interface, select &lt;strong&gt;Help&lt;/strong&gt; &amp;gt; &lt;strong&gt;User Interface Tour&lt;/strong&gt; from the top navigation menu to learn more.&lt;/p&gt;

&lt;p&gt;From here, you may be interested to read our series on &lt;a href="https://www.digitalocean.com/community/tutorial_series/time-series-visualization-and-forecasting"&gt;Time Series Visualization and Forecasting&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-set-up-an-openvpn-server-on-debian-9</id>
    <published>2018-09-07T21:53:50Z</published>
    <updated>2018-09-07T21:58:18Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-set-up-an-openvpn-server-on-debian-9"/>
    <title>How To Set Up an OpenVPN Server on Debian 9</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;Want to access the Internet safely and securely from your smartphone or laptop when connected to an untrusted network such as the WiFi of a hotel or coffee shop? A &lt;a href="https://en.wikipedia.org/wiki/Virtual_private_network"&gt;Virtual Private Network&lt;/a&gt; (VPN) allows you to traverse untrusted networks privately and securely as if you were on a private network. The traffic emerges from the VPN server and continues its journey to the destination. &lt;/p&gt;

&lt;p&gt;When combined with &lt;a href="https://en.wikipedia.org/wiki/HTTP_Secure"&gt;HTTPS connections&lt;/a&gt;, this setup allows you to secure your wireless logins and transactions. You can circumvent geographical restrictions and censorship, and shield your location and any unencrypted HTTP traffic from the untrusted network.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://openvpn.net"&gt;OpenVPN&lt;/a&gt; is a full-featured, open-source Secure Socket Layer (SSL) VPN solution that accommodates a wide range of configurations. In this tutorial, you will set up an OpenVPN server on a Debian 9 server and then configure access to it from Windows, macOS, iOS and/or Android. This tutorial will keep the installation and configuration steps as simple as possible for each of these setups.&lt;/p&gt;

&lt;span class='note'&gt;&lt;p&gt;
&lt;strong&gt;Note:&lt;/strong&gt; If you plan to set up an OpenVPN server on a DigitalOcean Droplet, be aware that we, like many hosting providers, charge for bandwidth overages. For this reason, please be mindful of how much traffic your server is handling.&lt;/p&gt;

&lt;p&gt;See &lt;a href="https://www.digitalocean.com/docs/accounts/billing/bandwidth/"&gt;this page&lt;/a&gt; for more info.&lt;br&gt;&lt;/p&gt;&lt;/span&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;To complete this tutorial, you will need access to a Debian 9 server to host your OpenVPN service. You will need to configure a non-&lt;strong&gt;root&lt;/strong&gt; user with &lt;code&gt;sudo&lt;/code&gt; privileges before you start this guide. You can follow our &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-debian-9"&gt;Debian 9 initial server setup guide&lt;/a&gt; to set up a user with appropriate permissions. The linked tutorial will also set up a &lt;strong&gt;firewall&lt;/strong&gt;, which is assumed to be in place throughout this guide.&lt;/p&gt;

&lt;p&gt;Additionally, you will need a separate machine to serve as your certificate authority (CA). While it’s technically possible to use your OpenVPN server or your local machine as your CA, this is not recommended as it opens up your VPN to some security vulnerabilities. Per &lt;a href="https://openvpn.net/index.php/open-source/documentation/"&gt;the official OpenVPN documentation&lt;/a&gt;, you should place your CA on a standalone machine that’s dedicated to importing and signing certificate requests. For this reason, this guide assumes that your CA is on a separate Debian 9 server that also has a non-&lt;strong&gt;root&lt;/strong&gt; user with &lt;code&gt;sudo&lt;/code&gt; privileges and a basic firewall.&lt;/p&gt;

&lt;p&gt;Please note that if you disable password authentication while configuring these servers, you may run into difficulties when transferring files between them later on in this guide. To resolve this issue, you could re-enable password authentication on each server. Alternatively, you could generate an SSH keypair for each server, then add the OpenVPN server’s public SSH key to the CA machine’s &lt;code&gt;authorized_keys&lt;/code&gt; file and vice versa. See &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-set-up-ssh-keys-on-debian-9"&gt;How to Set Up SSH Keys on Debian 9&lt;/a&gt; for instructions on how to perform either of these solutions.&lt;/p&gt;

&lt;p&gt;When you have these prerequisites in place, you can move on to Step 1 of this tutorial.&lt;/p&gt;

&lt;h2 id="step-1-—-installing-openvpn-and-easyrsa"&gt;Step 1 — Installing OpenVPN and EasyRSA&lt;/h2&gt;

&lt;p&gt;To start off, update your &lt;strong&gt;VPN server’s&lt;/strong&gt; package index and install OpenVPN. OpenVPN is available in Debian's default repositories, so you can use &lt;code&gt;apt&lt;/code&gt; for the installation:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt update
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo apt install openvpn
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;OpenVPN is a TLS/SSL VPN. This means that it utilizes certificates in order to encrypt traffic between the server and clients. To issue trusted certificates, you will set up your own simple certificate authority (CA). To do this, we will download the latest version of EasyRSA, which we’ll use to build our CA public key infrastructure (PKI), from the project’s official GitHub repository. &lt;/p&gt;

&lt;p&gt;As mentioned in the prerequisites, we will build the CA on a standalone server. The reason for this approach is that, if an attacker were able to infiltrate your server, they would be able to access your CA private key and use it to sign new certificates, giving them access to your VPN. Accordingly, managing the CA from a standalone machine helps to prevent unauthorized users from accessing your VPN. Note, as well, that it’s recommended that you keep the CA server turned off when not being used to sign keys as a further precautionary measure.&lt;/p&gt;

&lt;p&gt;To begin building the CA and PKI infrastructure, install the latest version of EasyRSA from the &lt;a href="https://github.com/OpenVPN/easy-rsa"&gt;official GitHub project&lt;/a&gt; on &lt;strong&gt;both&lt;/strong&gt; your CA machine and your OpenVPN server with the following command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;wget -P ~/ https://github.com/OpenVPN/easy-rsa/releases/download/v&lt;span class="highlight"&gt;3.0.4&lt;/span&gt;/EasyRSA-&lt;span class="highlight"&gt;3.0.4&lt;/span&gt;.tgz
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then extract the tarball:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cd ~
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;tar xvf EasyRSA-&lt;span class="highlight"&gt;3.0.4&lt;/span&gt;.tgz
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You have successfully installed all the required software on your server and CA machine. Continue on to configure the variables used by EasyRSA and to set up a CA directory, from which you will generate the keys and certificates needed for your server and clients to access the VPN.&lt;/p&gt;

&lt;h2 id="step-2-—-configuring-the-easyrsa-variables-and-building-the-ca"&gt;Step 2 — Configuring the EasyRSA Variables and Building the CA&lt;/h2&gt;

&lt;p&gt;EasyRSA comes installed with a configuration file which you can edit to define a number of variables for your CA. &lt;/p&gt;

&lt;p&gt;On your &lt;strong&gt;CA machine&lt;/strong&gt;, navigate to the EasyRSA directory:&lt;/p&gt;
&lt;pre class="code-pre command second-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cd ~/EasyRSA-&lt;span class="highlight"&gt;3.0.4&lt;/span&gt;/
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Inside this directory is a file named &lt;code&gt;vars.example&lt;/code&gt;. Make a copy of this file, and name the copy &lt;code&gt;vars&lt;/code&gt; without a file extension:&lt;/p&gt;
&lt;pre class="code-pre command second-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cp vars.example vars
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Open this new file using your preferred text editor:&lt;/p&gt;
&lt;pre class="code-pre command second-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nano vars
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Find the settings that set field defaults for new certificates. It will look something like this:&lt;/p&gt;
&lt;div class="code-label " title="~/EasyRSA-3.0.4/vars"&gt;~/EasyRSA-3.0.4/vars&lt;/div&gt;&lt;pre class="code-pre  second-environment"&gt;&lt;code langs=""&gt;. . .

#set_var EASYRSA_REQ_COUNTRY    "US"
#set_var EASYRSA_REQ_PROVINCE   "California"
#set_var EASYRSA_REQ_CITY       "San Francisco"
#set_var EASYRSA_REQ_ORG        "Copyleft Certificate Co"
#set_var EASYRSA_REQ_EMAIL      "me@example.net"
#set_var EASYRSA_REQ_OU         "My Organizational Unit"

. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Uncomment these lines and update the highlighted values to whatever you'd prefer, but do not leave them blank:&lt;/p&gt;
&lt;div class="code-label " title="~/EasyRSA-3.0.4/vars"&gt;~/EasyRSA-3.0.4/vars&lt;/div&gt;&lt;pre class="code-pre  second-environment"&gt;&lt;code langs=""&gt;. . .

set_var EASYRSA_REQ_COUNTRY    "&lt;span class="highlight"&gt;US&lt;/span&gt;"
set_var EASYRSA_REQ_PROVINCE   "&lt;span class="highlight"&gt;NewYork&lt;/span&gt;"
set_var EASYRSA_REQ_CITY       "&lt;span class="highlight"&gt;New York City&lt;/span&gt;"
set_var EASYRSA_REQ_ORG        "&lt;span class="highlight"&gt;DigitalOcean&lt;/span&gt;"
set_var EASYRSA_REQ_EMAIL      "&lt;span class="highlight"&gt;admin@example.com&lt;/span&gt;"
set_var EASYRSA_REQ_OU         "&lt;span class="highlight"&gt;Community&lt;/span&gt;"

. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When you are finished, save and close the file.&lt;/p&gt;

&lt;p&gt;Within the EasyRSA directory is a script called &lt;code&gt;easyrsa&lt;/code&gt; which is called to perform a variety of tasks involved with building and managing the CA. Run this script with the &lt;code&gt;init-pki&lt;/code&gt; option to initiate the public key infrastructure on the CA server:&lt;/p&gt;
&lt;pre class="code-pre command second-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;./easyrsa init-pki
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre  second-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;. . .
init-pki complete; you may now create a CA or requests.
Your newly created PKI dir is: /home/sammy/EasyRSA-3.0.4/pki
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After this, call the &lt;code&gt;easyrsa&lt;/code&gt; script again, following it with the &lt;code&gt;build-ca&lt;/code&gt; option. This will build the CA and create two important files — &lt;code&gt;ca.crt&lt;/code&gt; and &lt;code&gt;ca.key&lt;/code&gt; — which make up the public and private sides of an SSL certificate. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ca.crt&lt;/code&gt; is the CA’s public certificate file which, in the context of OpenVPN, the server and the client use to inform one another that they are part of the same web of trust and not someone performing a man-in-the-middle attack. For this reason, your server and all of your clients will need a copy of the &lt;code&gt;ca.crt&lt;/code&gt; file. &lt;/li&gt;
&lt;li&gt;&lt;code&gt;ca.key&lt;/code&gt; is the private key which the CA machine uses to sign keys and certificates for servers and clients. If an attacker gains access to your CA and, in turn, your &lt;code&gt;ca.key&lt;/code&gt; file, they will be able to sign certificate requests and gain access to your VPN, impeding its security. This is why your &lt;code&gt;ca.key&lt;/code&gt; file should &lt;strong&gt;only&lt;/strong&gt; be on your CA machine and that, ideally, your CA machine should be kept offline when not signing certificate requests as an extra security measure.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you don’t want to be prompted for a password every time you interact with your CA, you can run the &lt;code&gt;build-ca&lt;/code&gt; command with the &lt;code&gt;nopass&lt;/code&gt; option, like this:&lt;/p&gt;
&lt;pre class="code-pre command second-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;./easyrsa build-ca nopass
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the output, you’ll be asked to confirm the &lt;em&gt;common name&lt;/em&gt; for your CA:&lt;/p&gt;
&lt;pre class="code-pre  second-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;. . .
Common Name (eg: your user, host, or server name) [Easy-RSA CA]:
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The common name is the name used to refer to this machine in the context of the certificate authority. You can enter any string of characters for the CA’s common name but, for simplicity’s sake, press &lt;code&gt;ENTER&lt;/code&gt; to accept the default name.&lt;/p&gt;

&lt;p&gt;With that, your CA is in place and it’s ready to start signing certificate requests. &lt;/p&gt;

&lt;h2 id="step-3-—-creating-the-server-certificate-key-and-encryption-files"&gt;Step 3 — Creating the Server Certificate, Key, and Encryption Files&lt;/h2&gt;

&lt;p&gt;Now that you have a CA ready to go, you can generate a private key and certificate request from your server and then transfer the request over to your CA to be signed, creating the required certificate. You’re also free to create some additional files used during the encryption process.&lt;/p&gt;

&lt;p&gt;Start by navigating to the EasyRSA directory on your &lt;strong&gt;OpenVPN server&lt;/strong&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cd EasyRSA-&lt;span class="highlight"&gt;3.0.4&lt;/span&gt;/
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;From there, run the &lt;code&gt;easyrsa&lt;/code&gt; script with the &lt;code&gt;init-pki&lt;/code&gt; option. Although you already ran this command on the CA machine, it’s necessary to run it here because your server and CA will have separate PKI directories:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;./easyrsa init-pki
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then call the &lt;code&gt;easyrsa&lt;/code&gt; script again, this time with the &lt;code&gt;gen-req&lt;/code&gt; option followed by a common name for the machine. Again, this could be anything you like but it can be helpful to make it something descriptive. Throughout this tutorial, the OpenVPN server’s common name will simply be “server”. Be sure to include the &lt;code&gt;nopass&lt;/code&gt; option as well. Failing to do so will password-protect the request file which could lead to permissions issues later on:&lt;/p&gt;

&lt;p&gt;&lt;span class='note'&gt;&lt;strong&gt;Note&lt;/strong&gt;: If you choose a name other than “server” here, you will have to adjust some of the instructions below. For instance, when copying the generated files to the &lt;code&gt;/etc/openvpn&lt;/code&gt; directory, you will have to substitute the correct names. You will also have to modify the &lt;code&gt;/etc/openvpn/server.conf&lt;/code&gt; file later to point to the correct &lt;code&gt;.crt&lt;/code&gt; and &lt;code&gt;.key&lt;/code&gt; files.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;./easyrsa gen-req server nopass
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will create a private key for the server and a certificate request file called &lt;code&gt;server.req&lt;/code&gt;. Copy the server key to the &lt;code&gt;/etc/openvpn/&lt;/code&gt; directory:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo cp ~/EasyRSA-&lt;span class="highlight"&gt;3.0.4&lt;/span&gt;/pki/private/server.key /etc/openvpn/
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Using a secure method (like SCP, in our example below), transfer the &lt;code&gt;server.req&lt;/code&gt; file to your CA machine:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;scp ~/EasyRSA-3.0.4/pki/reqs/server.req &lt;span class="highlight"&gt;sammy&lt;/span&gt;@&lt;span class="highlight"&gt;your_CA_ip&lt;/span&gt;:/tmp
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, on &lt;strong&gt;your CA machine&lt;/strong&gt;, navigate to the EasyRSA directory:&lt;/p&gt;
&lt;pre class="code-pre command second-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cd EasyRSA-&lt;span class="highlight"&gt;3.0.4&lt;/span&gt;/
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Using the &lt;code&gt;easyrsa&lt;/code&gt; script again, import the &lt;code&gt;server.req&lt;/code&gt; file, following the file path with its common name:&lt;/p&gt;
&lt;pre class="code-pre command second-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;./easyrsa import-req /tmp/server.req server
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then sign the request by running the &lt;code&gt;easyrsa&lt;/code&gt; script with the &lt;code&gt;sign-req&lt;/code&gt; option, followed by the &lt;em&gt;request type&lt;/em&gt; and the common name. The request type can either be &lt;code&gt;client&lt;/code&gt; or &lt;code&gt;server&lt;/code&gt;, so for the OpenVPN server’s certificate request, be sure to use the &lt;code&gt;server&lt;/code&gt; request type:&lt;/p&gt;
&lt;pre class="code-pre command second-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;./easyrsa sign-req server server
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the output, you’ll be asked to verify that the request comes from a trusted source. Type &lt;code&gt;yes&lt;/code&gt; then press &lt;code&gt;ENTER&lt;/code&gt; to confirm this:&lt;/p&gt;
&lt;pre class="code-pre  second-environment"&gt;&lt;code langs=""&gt;You are about to sign the following certificate.
Please check over the details shown below for accuracy. Note that this request
has not been cryptographically verified. Please be sure it came from a trusted
source or that you have verified the request checksum with the sender.

Request subject, to be signed as a server certificate for 3650 days:

subject=
    commonName                = server


Type the word 'yes' to continue, or any other input to abort.
  Confirm request details: &lt;span class="highlight"&gt;yes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you encrypted your CA key, you’ll be prompted for your password at this point.&lt;/p&gt;

&lt;p&gt;Next, transfer the signed certificate back to your VPN server using a secure method:&lt;/p&gt;
&lt;pre class="code-pre command second-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;scp pki/issued/server.crt &lt;span class="highlight"&gt;sammy&lt;/span&gt;@&lt;span class="highlight"&gt;your_server_ip&lt;/span&gt;:/tmp
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Before logging out of your CA machine, transfer the &lt;code&gt;ca.crt&lt;/code&gt; file to your server as well:&lt;/p&gt;
&lt;pre class="code-pre command second-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;scp pki/ca.crt &lt;span class="highlight"&gt;sammy&lt;/span&gt;@&lt;span class="highlight"&gt;your_server_ip&lt;/span&gt;:/tmp
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, log back into your OpenVPN server and copy the &lt;code&gt;server.crt&lt;/code&gt; and &lt;code&gt;ca.crt&lt;/code&gt; files into your &lt;code&gt;/etc/openvpn/&lt;/code&gt; directory:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo cp /tmp/{server.crt,ca.crt} /etc/openvpn/
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then navigate to your EasyRSA directory:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cd EasyRSA-&lt;span class="highlight"&gt;3.0.4&lt;/span&gt;/
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;From there, create a strong Diffie-Hellman key to use during key exchange by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;./easyrsa gen-dh
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This may take a few minutes to complete. Once it does, generate an HMAC signature to strengthen the server's TLS integrity verification capabilities:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo openvpn --genkey --secret ta.key
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When the command finishes, copy the two new files to your &lt;code&gt;/etc/openvpn/&lt;/code&gt; directory:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo cp ~/EasyRSA-&lt;span class="highlight"&gt;3.0.4&lt;/span&gt;/ta.key /etc/openvpn/
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo cp ~/EasyRSA-&lt;span class="highlight"&gt;3.0.4&lt;/span&gt;/pki/dh.pem /etc/openvpn/
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With that, all the certificate and key files needed by your server have been generated. You’re ready to create the corresponding certificates and keys which your client machine will use to access your OpenVPN server.&lt;/p&gt;

&lt;h2 id="step-4-—-generating-a-client-certificate-and-key-pair"&gt;Step 4 — Generating a Client Certificate and Key Pair&lt;/h2&gt;

&lt;p&gt;Although you can generate a private key and certificate request on your client machine and then send it to the CA to be signed, this guide outlines a process for generating the certificate request on the server. The benefit of this is that we can create a script which will automatically generate client configuration files that contain all of the required keys and certificates. This lets you avoid having to transfer keys, certificates, and configuration files to clients and streamlines the process of joining the VPN.&lt;/p&gt;

&lt;p&gt;We will generate a single client key and certificate pair for this guide. If you have more than one client, you can repeat this process for each one. Please note, though, that you will need to pass a unique name value to the script for every client. Throughout this tutorial, the first certificate/key pair is referred to as &lt;code&gt;client1&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Get started by creating a directory structure within your home directory to store the client certificate and key files:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;mkdir -p ~/client-configs/keys
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since you will store your clients’ certificate/key pairs and configuration files in this directory, you should lock down its permissions now as a security measure:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;chmod -R 700 ~/client-configs
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, navigate back to the EasyRSA directory and run the &lt;code&gt;easyrsa&lt;/code&gt; script with the &lt;code&gt;gen-req&lt;/code&gt; and &lt;code&gt;nopass&lt;/code&gt; options, along with the common name for the client:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cd ~/EasyRSA-&lt;span class="highlight"&gt;3.0.4&lt;/span&gt;/
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;./easyrsa gen-req client1 nopass
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Press &lt;code&gt;ENTER&lt;/code&gt; to confirm the common name. Then, copy the &lt;code&gt;client1.key&lt;/code&gt; file to the &lt;code&gt;/client-configs/keys/&lt;/code&gt; directory you created earlier:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cp pki/private/client1.key ~/client-configs/keys/
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, transfer the &lt;code&gt;client1.req&lt;/code&gt; file to your CA machine using a secure method:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;scp pki/reqs/client1.req &lt;span class="highlight"&gt;sammy&lt;/span&gt;@&lt;span class="highlight"&gt;your_CA_ip&lt;/span&gt;:/tmp
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Log in to your CA machine, navigate to the EasyRSA directory, and import the certificate request:&lt;/p&gt;
&lt;pre class="code-pre command second-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;ssh &lt;span class="highlight"&gt;sammy&lt;/span&gt;@&lt;span class="highlight"&gt;your_CA_IP&lt;/span&gt;
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;cd EasyRSA-&lt;span class="highlight"&gt;3.0.4&lt;/span&gt;/
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;./easyrsa import-req /tmp/client1.req client1
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then sign the request as you did for the server in the previous step. This time, though, be sure to specify the &lt;code&gt;client&lt;/code&gt; request type:&lt;/p&gt;
&lt;pre class="code-pre command second-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;./easyrsa sign-req client client1
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At the prompt, enter &lt;code&gt;yes&lt;/code&gt; to confirm that you intend to sign the certificate request and that it came from a trusted source:&lt;/p&gt;
&lt;pre class="code-pre  second-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Type the word 'yes' to continue, or any other input to abort.
  Confirm request details: &lt;span class="highlight"&gt;yes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Again, if you encrypted your CA key, you’ll be prompted for your password here.&lt;/p&gt;

&lt;p&gt;This will create a client certificate file named &lt;code&gt;client1.crt&lt;/code&gt;. Transfer this file back to the server:&lt;/p&gt;
&lt;pre class="code-pre command second-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;scp pki/issued/client1.crt &lt;span class="highlight"&gt;sammy&lt;/span&gt;@&lt;span class="highlight"&gt;your_server_ip&lt;/span&gt;:/tmp
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;SSH back into your OpenVPN server and copy the client certificate to the &lt;code&gt;/client-configs/keys/&lt;/code&gt; directory:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cp /tmp/client1.crt ~/client-configs/keys/
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, copy the &lt;code&gt;ca.crt&lt;/code&gt; and &lt;code&gt;ta.key&lt;/code&gt; files to the &lt;code&gt;/client-configs/keys/&lt;/code&gt; directory as well:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo cp EasyRSA-&lt;span class="highlight"&gt;3.0.4&lt;/span&gt;/ta.key ~/client-configs/keys/
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo cp /etc/openvpn/ca.crt ~/client-configs/keys/
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With that, your server and client’s certificates and keys have all been generated and are stored in the appropriate directories on your server. There are still a few actions that need to be performed with these files, but those will come in a later step. For now, you can move on to configuring OpenVPN on your server.&lt;/p&gt;

&lt;h2 id="step-5-—-configuring-the-openvpn-service"&gt;Step 5 — Configuring the OpenVPN Service&lt;/h2&gt;

&lt;p&gt;Now that both your client and server’s certificates and keys have been generated, you can begin configuring the OpenVPN service to use these credentials. &lt;/p&gt;

&lt;p&gt;Start by copying a sample OpenVPN configuration file into the configuration directory and then extract it in order to use it as a basis for your setup:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo cp /usr/share/doc/openvpn/examples/sample-config-files/server.conf.gz /etc/openvpn/
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo gzip -d /etc/openvpn/server.conf.gz
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Open the server configuration file in your preferred text editor:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/openvpn/server.conf
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Find the HMAC section by looking for the &lt;code&gt;tls-auth&lt;/code&gt; directive. This line should already be uncommented, but if isn’t then remove the "&lt;strong&gt;;&lt;/strong&gt;" to uncomment it. Below this line, add the &lt;code&gt;key-direction&lt;/code&gt; parameter, set to "0":&lt;/p&gt;
&lt;div class="code-label " title="/etc/openvpn/server.conf"&gt;/etc/openvpn/server.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;tls-auth ta.key 0 # This file is secret
&lt;span class="highlight"&gt;key-direction 0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, find the section on cryptographic ciphers by looking for the commented out &lt;code&gt;cipher&lt;/code&gt; lines. The &lt;code&gt;AES-256-CBC&lt;/code&gt; cipher offers a good level of encryption and is well supported. Again, this line should already be uncommented, but if it isn’t then just remove the "&lt;strong&gt;;&lt;/strong&gt;" preceding it:&lt;/p&gt;
&lt;div class="code-label " title="/etc/openvpn/server.conf"&gt;/etc/openvpn/server.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;cipher AES-256-CBC
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Below this, add an &lt;code&gt;auth&lt;/code&gt; directive to select the HMAC message digest algorithm. For this, &lt;code&gt;SHA256&lt;/code&gt; is a good choice:&lt;/p&gt;
&lt;div class="code-label " title="/etc/openvpn/server.conf"&gt;/etc/openvpn/server.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;span class="highlight"&gt;auth SHA256&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, find the line containing a &lt;code&gt;dh&lt;/code&gt; directive which defines the Diffie-Hellman parameters. Because of some recent changes made to EasyRSA, the filename for the Diffie-Hellman key may be different than what is listed in the example server configuration file. If necessary, change the file name listed here by removing the &lt;code&gt;2048&lt;/code&gt; so it aligns with the key you generated in the previous step:&lt;/p&gt;
&lt;div class="code-label " title="/etc/openvpn/server.conf"&gt;/etc/openvpn/server.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;dh dh.pem
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, find the &lt;code&gt;user&lt;/code&gt; and &lt;code&gt;group&lt;/code&gt; settings and remove the "&lt;strong&gt;;&lt;/strong&gt;" at the beginning of each to uncomment these lines:&lt;/p&gt;
&lt;div class="code-label " title="/etc/openvpn/server.conf"&gt;/etc/openvpn/server.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;user nobody
group nogroup
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The changes you’ve made to the sample &lt;code&gt;server.conf&lt;/code&gt; file up to this point are necessary in order for OpenVPN to function. The changes outlined below are optional, though they too are needed for many common use cases.&lt;/p&gt;

&lt;h3 id="optional-push-dns-changes-to-redirect-all-traffic-through-the-vpn"&gt;(Optional) Push DNS Changes to Redirect All Traffic Through the VPN&lt;/h3&gt;

&lt;p&gt;The settings above will create the VPN connection between the two machines, but will not force any connections to use the tunnel. If you wish to use the VPN to route all of your traffic, you will likely want to push the DNS settings to the client computers.&lt;/p&gt;

&lt;p&gt;There are a few directives in the &lt;code&gt;server.conf&lt;/code&gt; file which you must change in order to enable this functionality. Find the &lt;code&gt;redirect-gateway&lt;/code&gt; section and remove the semicolon "&lt;strong&gt;;&lt;/strong&gt;" from the beginning of the &lt;code&gt;redirect-gateway&lt;/code&gt; line to uncomment it:&lt;/p&gt;
&lt;div class="code-label " title="/etc/openvpn/server.conf"&gt;/etc/openvpn/server.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;push "redirect-gateway def1 bypass-dhcp"
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Just below this, find the &lt;code&gt;dhcp-option&lt;/code&gt; section. Again, remove the "&lt;strong&gt;;&lt;/strong&gt;" from in front of both of the lines to uncomment them:&lt;/p&gt;
&lt;div class="code-label " title="/etc/openvpn/server.conf"&gt;/etc/openvpn/server.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;push "dhcp-option DNS 208.67.222.222"
push "dhcp-option DNS 208.67.220.220"
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will assist clients in reconfiguring their DNS settings to use the VPN tunnel for as the default gateway.&lt;/p&gt;

&lt;h3 id="optional-adjust-the-port-and-protocol"&gt;(Optional) Adjust the Port and Protocol&lt;/h3&gt;

&lt;p&gt;By default, the OpenVPN server uses port &lt;code&gt;1194&lt;/code&gt; and the UDP protocol to accept client connections. If you need to use a different port because of restrictive network environments that your clients might be in, you can change the &lt;code&gt;port&lt;/code&gt; option. If you are not hosting web content on your OpenVPN server, port &lt;code&gt;443&lt;/code&gt; is a popular choice since it is usually allowed through firewall rules.&lt;/p&gt;
&lt;div class="code-label " title="/etc/openvpn/server.conf"&gt;/etc/openvpn/server.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;# Optional!
port &lt;span class="highlight"&gt;443&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Oftentimes, the protocol is restricted to that port as well. If so, change &lt;code&gt;proto&lt;/code&gt; from UDP to TCP:&lt;/p&gt;
&lt;div class="code-label " title="/etc/openvpn/server.conf"&gt;/etc/openvpn/server.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;# Optional!
proto &lt;span class="highlight"&gt;tcp&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you &lt;strong&gt;do&lt;/strong&gt; switch the protocol to TCP, you will need to change the &lt;code&gt;explicit-exit-notify&lt;/code&gt; directive’s value from &lt;code&gt;1&lt;/code&gt; to &lt;code&gt;0&lt;/code&gt;, as this directive is only used by UDP. Failing to do so while using TCP will cause errors when you start the OpenVPN service:&lt;/p&gt;
&lt;div class="code-label " title="/etc/openvpn/server.conf"&gt;/etc/openvpn/server.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;# Optional!
explicit-exit-notify &lt;span class="highlight"&gt;0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you have no need to use a different port and protocol, it is best to leave these two settings as their defaults.&lt;/p&gt;

&lt;h3 id="optional-point-to-non-default-credentials"&gt;(Optional) Point to Non-Default Credentials&lt;/h3&gt;

&lt;p&gt;If you selected a different name during the &lt;code&gt;./build-key-server&lt;/code&gt; command earlier, modify the &lt;code&gt;cert&lt;/code&gt; and &lt;code&gt;key&lt;/code&gt; lines that you see to point to the appropriate &lt;code&gt;.crt&lt;/code&gt; and &lt;code&gt;.key&lt;/code&gt; files. If you used the default name, “server”, this is already set correctly:&lt;/p&gt;
&lt;div class="code-label " title="/etc/openvpn/server.conf"&gt;/etc/openvpn/server.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;cert &lt;span class="highlight"&gt;server&lt;/span&gt;.crt
key &lt;span class="highlight"&gt;server&lt;/span&gt;.key
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When you are finished, save and close the file.&lt;/p&gt;

&lt;p&gt;After going through and making whatever changes to your server’s OpenVPN configuration are required for your specific use case, you can begin making some changes to your server’s networking.&lt;/p&gt;

&lt;h2 id="step-6-—-adjusting-the-server-networking-configuration"&gt;Step 6 — Adjusting the Server Networking Configuration&lt;/h2&gt;

&lt;p&gt;There are some aspects of the server’s networking configuration that need to be tweaked so that OpenVPN can correctly route traffic through the VPN. The first of these is &lt;em&gt;IP forwarding&lt;/em&gt;, a method for determining where IP traffic should be routed. This is essential to the VPN functionality that your server will provide.&lt;/p&gt;

&lt;p&gt;Adjust your server’s default IP forwarding setting by modifying the &lt;code&gt;/etc/sysctl.conf&lt;/code&gt; file:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/sysctl.conf
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Inside, look for the commented line that sets &lt;code&gt;net.ipv4.ip_forward&lt;/code&gt;. Remove the "&lt;strong&gt;#&lt;/strong&gt;" character from the beginning of the line to uncomment this setting:&lt;/p&gt;
&lt;div class="code-label " title="/etc/sysctl.conf"&gt;/etc/sysctl.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;net.ipv4.ip_forward=1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save and close the file when you are finished.&lt;/p&gt;

&lt;p&gt;To read the file and adjust the values for the current session, type:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo sysctl -p
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;net.ipv4.ip_forward = 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you followed the Debian 9 initial server setup guide listed in the prerequisites, you should have a UFW firewall in place. Regardless of whether you use the firewall to block unwanted traffic (which you almost always should do), for this guide you need a firewall to manipulate some of the traffic coming into the server. Some of the firewall rules must be modified to enable masquerading, an iptables concept that provides on-the-fly dynamic network address translation (NAT) to correctly route client connections.&lt;/p&gt;

&lt;p&gt;Before opening the firewall configuration file to add the masquerading rules, you must first find the public network interface of your machine. To do this, type:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;ip route | grep default
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Your public interface is the string found within this command’s output that follows the word "dev". For example, this result shows the interface named &lt;code&gt;eth0&lt;/code&gt;, which is highlighted below:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;default via 203.0.113.1 dev &lt;span class="highlight"&gt;eth0&lt;/span&gt; onlink
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When you have the interface associated with your default route, open the &lt;code&gt;/etc/ufw/before.rules&lt;/code&gt; file to add the relevant configuration:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/ufw/before.rules
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;UFW rules are typically added using the &lt;code&gt;ufw&lt;/code&gt; command. Rules listed in the &lt;code&gt;before.rules&lt;/code&gt; file, though, are read and put into place before the conventional UFW rules are loaded. Towards the top of the file, add the highlighted lines below. This will set the default policy for the &lt;code&gt;POSTROUTING&lt;/code&gt; chain in the &lt;code&gt;nat&lt;/code&gt; table and masquerade any traffic coming from the VPN. Remember to replace &lt;code&gt;&lt;span class="highlight"&gt;eth0&lt;/span&gt;&lt;/code&gt; in the &lt;code&gt;-A POSTROUTING&lt;/code&gt; line below with the interface you found in the above command:&lt;/p&gt;
&lt;div class="code-label " title="/etc/ufw/before.rules"&gt;/etc/ufw/before.rules&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;#
# rules.before
#
# Rules that should be run before the ufw command line added rules. Custom
# rules should be added to one of these chains:
#   ufw-before-input
#   ufw-before-output
#   ufw-before-forward
#

&lt;span class="highlight"&gt;# START OPENVPN RULES&lt;/span&gt;
&lt;span class="highlight"&gt;# NAT table rules&lt;/span&gt;
&lt;span class="highlight"&gt;*nat&lt;/span&gt;
&lt;span class="highlight"&gt;:POSTROUTING ACCEPT [0:0]&lt;/span&gt; 
&lt;span class="highlight"&gt;# Allow traffic from OpenVPN client to &lt;/span&gt;eth0&lt;span class="highlight"&gt; (change to the interface you discovered!)&lt;/span&gt;
&lt;span class="highlight"&gt;-A POSTROUTING -s 10.8.0.0/8 -o &lt;/span&gt;eth0&lt;span class="highlight"&gt; -j MASQUERADE&lt;/span&gt;
&lt;span class="highlight"&gt;COMMIT&lt;/span&gt;
&lt;span class="highlight"&gt;# END OPENVPN RULES&lt;/span&gt;

# Don't delete these required lines, otherwise there will be errors
*filter
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save and close the file when you are finished.&lt;/p&gt;

&lt;p&gt;Next, you need to tell UFW to allow forwarded packets by default as well. To do this, open the &lt;code&gt;/etc/default/ufw&lt;/code&gt; file:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/default/ufw
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Inside, find the &lt;code&gt;DEFAULT_FORWARD_POLICY&lt;/code&gt; directive and change the value from &lt;code&gt;DROP&lt;/code&gt; to &lt;code&gt;ACCEPT&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-label " title="/etc/default/ufw"&gt;/etc/default/ufw&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;DEFAULT_FORWARD_POLICY="&lt;span class="highlight"&gt;ACCEPT&lt;/span&gt;"
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save and close the file when you are finished.&lt;/p&gt;

&lt;p&gt;Next, adjust the firewall itself to allow traffic to OpenVPN. If you did not change the port and protocol in the &lt;code&gt;/etc/openvpn/server.conf&lt;/code&gt; file, you will need to open up UDP traffic to port &lt;code&gt;1194&lt;/code&gt;. If you modified the port and/or protocol, substitute the values you selected here.&lt;/p&gt;

&lt;p&gt;In case you forgot to add the SSH port when following the prerequisite tutorial, add it here as well:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw allow &lt;span class="highlight"&gt;1194&lt;/span&gt;/&lt;span class="highlight"&gt;udp&lt;/span&gt;
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo ufw allow OpenSSH
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After adding those rules, disable and re-enable UFW to restart it and load the changes from all of the files you've modified:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw disable
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo ufw enable
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Your server is now configured to correctly handle OpenVPN traffic.&lt;/p&gt;

&lt;h2 id="step-7-—-starting-and-enabling-the-openvpn-service"&gt;Step 7 — Starting and Enabling the OpenVPN Service&lt;/h2&gt;

&lt;p&gt;You're finally ready to start the OpenVPN service on your server. This is done using the systemd utility &lt;code&gt;systemctl&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Start the OpenVPN server by specifying your configuration file name as an instance variable after the systemd unit file name. The configuration file for your server is called &lt;code&gt;/etc/openvpn/&lt;span class="highlight"&gt;server&lt;/span&gt;.conf&lt;/code&gt;, so add &lt;code&gt;&lt;span class="highlight"&gt;@server&lt;/span&gt;&lt;/code&gt; to end of your unit file when calling it:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl start openvpn@&lt;span class="highlight"&gt;server&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Double-check that the service has started successfully by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl status openvpn@server
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If everything went well, your output will look something like this:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;● openvpn@server.service - OpenVPN connection to server
   Loaded: loaded (/lib/systemd/system/openvpn@.service; disabled; vendor preset: enabled)
   Active: &lt;span class="highlight"&gt;active (running)&lt;/span&gt; since Tue 2016-05-03 15:30:05 EDT; 47s ago
     Docs: man:openvpn(8)
           https://community.openvpn.net/openvpn/wiki/Openvpn23ManPage
           https://community.openvpn.net/openvpn/wiki/HOWTO
  Process: 5852 ExecStart=/usr/sbin/openvpn --daemon ovpn-%i --status /run/openvpn/%i.status 10 --cd /etc/openvpn --script-security 2 --config /etc/openvpn/%i.conf --writepid /run/openvpn/%i.pid (code=exited, sta
 Main PID: 5856 (openvpn)
    Tasks: 1 (limit: 512)
   CGroup: /system.slice/system-openvpn.slice/openvpn@server.service
           └─5856 /usr/sbin/openvpn --daemon ovpn-server --status /run/openvpn/server.status 10 --cd /etc/openvpn --script-security 2 --config /etc/openvpn/server.conf --writepid /run/openvpn/server.pid

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can also check that the OpenVPN &lt;code&gt;tun0&lt;/code&gt; interface is available by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;ip addr show tun0
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will output a configured interface:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;4: tun0: &amp;lt;POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP&amp;gt; mtu 1500 qdisc noqueue state UNKNOWN group default qlen 100
    link/none 
    inet 10.8.0.1 peer 10.8.0.2/32 scope global tun0
       valid_lft forever preferred_lft forever
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After starting the service, enable it so that it starts automatically at boot:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl enable openvpn@server
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Your OpenVPN service is now up and running. Before you can start using it, though, you must first create a configuration file for the client machine. This tutorial already went over how to create certificate/key pairs for clients, and in the next step we will demonstrate how to create an infrastructure that will generate client configuration files easily.&lt;/p&gt;

&lt;h2 id="step-8-—-creating-the-client-configuration-infrastructure"&gt;Step 8 — Creating the Client Configuration Infrastructure&lt;/h2&gt;

&lt;p&gt;Creating configuration files for OpenVPN clients can be somewhat involved, as every client must have its own config and each must align with the settings outlined in the server’s configuration file. Rather than writing a single configuration file that can only be used on one client, this step outlines a process for building a client configuration infrastructure which you can use to generate config files on-the-fly. You will first create a “base” configuration file then build a script which will allow you to generate unique client config files, certificates, and keys as needed.&lt;/p&gt;

&lt;p&gt;Get started by creating a new directory where you will store client configuration files within the &lt;code&gt;client-configs&lt;/code&gt; directory you created earlier:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;mkdir -p ~/client-configs/files
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, copy an example client configuration file into the &lt;code&gt;client-configs&lt;/code&gt; directory to use as your base configuration:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cp /usr/share/doc/openvpn/examples/sample-config-files/client.conf ~/client-configs/base.conf
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Open this new file in your text editor:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nano ~/client-configs/base.conf
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Inside, locate the &lt;code&gt;remote&lt;/code&gt; directive. This points the client to your OpenVPN server address — the public IP address of your OpenVPN server. If you decided to change the port that the OpenVPN server is listening on, you will also need to change &lt;code&gt;1194&lt;/code&gt; to the port you selected:&lt;/p&gt;
&lt;div class="code-label " title="~/client-configs/base.conf"&gt;~/client-configs/base.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .
# The hostname/IP and port of the server.
# You can have multiple remote entries
# to load balance between the servers.
remote &lt;span class="highlight"&gt;your_server_ip&lt;/span&gt; &lt;span class="highlight"&gt;1194&lt;/span&gt;
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Be sure that the protocol matches the value you are using in the server configuration:&lt;/p&gt;
&lt;div class="code-label " title="~/client-configs/base.conf"&gt;~/client-configs/base.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;proto &lt;span class="highlight"&gt;udp&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, uncomment the &lt;code&gt;user&lt;/code&gt; and &lt;code&gt;group&lt;/code&gt; directives by removing the "&lt;strong&gt;;&lt;/strong&gt;" at the beginning of each line:&lt;/p&gt;
&lt;div class="code-label " title="~/client-configs/base.conf"&gt;~/client-configs/base.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;# Downgrade privileges after initialization (non-Windows only)
user nobody
group nogroup
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Find the directives that set the &lt;code&gt;ca&lt;/code&gt;, &lt;code&gt;cert&lt;/code&gt;, and &lt;code&gt;key&lt;/code&gt;. Comment out these directives since you will add the certs and keys within the file itself shortly:&lt;/p&gt;
&lt;div class="code-label " title="~/client-configs/base.conf"&gt;~/client-configs/base.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;# SSL/TLS parms.
# See the server config file for more
# description.  It's best to use
# a separate .crt/.key file pair
# for each client.  A single ca
# file can be used for all clients.
&lt;span class="highlight"&gt;#&lt;/span&gt;ca ca.crt
&lt;span class="highlight"&gt;#&lt;/span&gt;cert client.crt
&lt;span class="highlight"&gt;#&lt;/span&gt;key client.key
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Mirror the &lt;code&gt;cipher&lt;/code&gt; and &lt;code&gt;auth&lt;/code&gt; settings that you set in the &lt;code&gt;/etc/openvpn/server.conf&lt;/code&gt; file:&lt;/p&gt;
&lt;div class="code-label " title="~/client-configs/base.conf"&gt;~/client-configs/base.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;span class="highlight"&gt;cipher AES-256-CBC&lt;/span&gt;
&lt;span class="highlight"&gt;auth SHA256&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, add the &lt;code&gt;key-direction&lt;/code&gt; directive somewhere in the file. You &lt;strong&gt;must&lt;/strong&gt; set this to "1" for the VPN to function correctly on the client machine:&lt;/p&gt;
&lt;div class="code-label " title="~/client-configs/base.conf"&gt;~/client-configs/base.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;span class="highlight"&gt;key-direction 1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, add a few &lt;strong&gt;commented out&lt;/strong&gt; lines. Although you can include these directives in every client configuration file, you only need to enable them for Linux clients that ship with an &lt;code&gt;/etc/openvpn/update-resolv-conf&lt;/code&gt; file. This script uses the &lt;code&gt;resolvconf&lt;/code&gt; utility to update DNS information for Linux clients.&lt;/p&gt;
&lt;div class="code-label " title="~/client-configs/base.conf"&gt;~/client-configs/base.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;span class="highlight"&gt;# script-security 2&lt;/span&gt;
&lt;span class="highlight"&gt;# up /etc/openvpn/update-resolv-conf&lt;/span&gt;
&lt;span class="highlight"&gt;# down /etc/openvpn/update-resolv-conf&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If your client is running Linux and has an &lt;code&gt;/etc/openvpn/update-resolv-conf&lt;/code&gt; file, uncomment these lines from the client’s configuration file after it has been generated.&lt;/p&gt;

&lt;p&gt;Save and close the file when you are finished.&lt;/p&gt;

&lt;p&gt;Next, create a simple script that will compile your base configuration with the relevant certificate, key, and encryption files and then place the generated configuration in the &lt;code&gt;~/client-configs/files&lt;/code&gt; directory. Open a new file called &lt;code&gt;make_config.sh&lt;/code&gt; within the &lt;code&gt;~/client-configs&lt;/code&gt; directory:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nano ~/client-configs/make_config.sh
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Inside, add the following content, making sure to change &lt;code&gt;&lt;span class="highlight"&gt;sammy&lt;/span&gt;&lt;/code&gt; to that of your server’s non-&lt;strong&gt;root&lt;/strong&gt; user account:&lt;/p&gt;
&lt;div class="code-label " title="~/client-configs/make_config.sh"&gt;~/client-configs/make_config.sh&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-sh"&gt;#!/bin/bash

# First argument: Client identifier

KEY_DIR=/home/&lt;span class="highlight"&gt;sammy&lt;/span&gt;/client-configs/keys
OUTPUT_DIR=/home/&lt;span class="highlight"&gt;sammy&lt;/span&gt;/client-configs/files
BASE_CONFIG=/home/&lt;span class="highlight"&gt;sammy&lt;/span&gt;/client-configs/base.conf

cat ${BASE_CONFIG} \
    &amp;lt;(echo -e '&amp;lt;ca&amp;gt;') \
    ${KEY_DIR}/ca.crt \
    &amp;lt;(echo -e '&amp;lt;/ca&amp;gt;\n&amp;lt;cert&amp;gt;') \
    ${KEY_DIR}/${1}.crt \
    &amp;lt;(echo -e '&amp;lt;/cert&amp;gt;\n&amp;lt;key&amp;gt;') \
    ${KEY_DIR}/${1}.key \
    &amp;lt;(echo -e '&amp;lt;/key&amp;gt;\n&amp;lt;tls-auth&amp;gt;') \
    ${KEY_DIR}/ta.key \
    &amp;lt;(echo -e '&amp;lt;/tls-auth&amp;gt;') \
    &amp;gt; ${OUTPUT_DIR}/${1}.ovpn
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save and close the file when you are finished.&lt;/p&gt;

&lt;p&gt;Before moving on, be sure to mark this file as executable by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;chmod 700 ~/client-configs/make_config.sh
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This script will make a copy of the &lt;code&gt;base.conf&lt;/code&gt; file you made, collect all the certificate and key files you’ve created for your client, extract their contents, append them to the copy of the base configuration file, and export all of this content into a new client configuration file. This means that, rather than having to manage the client’s configuration, certificate, and key files separately, all the required information is stored in one place. The benefit of this is that if you ever need to add a client in the future, you can just run this script to quickly create the config file and ensure that all the important information is stored in a single, easy-to-access location.&lt;/p&gt;

&lt;p&gt;Please note that any time you add a new client, you will need to generate new keys and certificates for it before you can run this script and generate its configuration file. You will get some practice using this script in the next step.&lt;/p&gt;

&lt;h2 id="step-9-—-generating-client-configurations"&gt;Step 9 — Generating Client Configurations&lt;/h2&gt;

&lt;p&gt;If you followed along with the guide, you created a client certificate and key named &lt;code&gt;client1.crt&lt;/code&gt; and &lt;code&gt;client1.key&lt;/code&gt;, respectively, in Step 4. You can generate a config file for these credentials by moving into your &lt;code&gt;~/client-configs&lt;/code&gt; directory and running the script you made at the end of the previous step:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cd ~/client-configs
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo ./make_config.sh &lt;span class="highlight"&gt;client1&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will create a file named &lt;code&gt;client1.ovpn&lt;/code&gt; in your &lt;code&gt;~/client-configs/files&lt;/code&gt; directory:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;ls ~/client-configs/files
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;client1.ovpn
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You need to transfer this file to the device you plan to use as the client. For instance, this could be your local computer or a mobile device.&lt;/p&gt;

&lt;p&gt;While the exact applications used to accomplish this transfer will depend on your device's operating system and your personal preferences, a dependable and secure method is to use SFTP (SSH file transfer protocol) or SCP (Secure Copy) on the backend. This will transport your client's VPN authentication files over an encrypted connection.&lt;/p&gt;

&lt;p&gt;Here is an example SFTP command using the &lt;code&gt;&lt;span class="highlight"&gt;client1.ovpn&lt;/span&gt;&lt;/code&gt; example which you can run from your local computer (macOS or Linux). It places the &lt;code&gt;.ovpn&lt;/code&gt; file in your home directory:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="local$"&gt;sftp &lt;span class="highlight"&gt;sammy&lt;/span&gt;@&lt;span class="highlight"&gt;your_server_ip&lt;/span&gt;:client-configs/files/client1.ovpn ~/
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here are several tools and tutorials for securely transferring files from the server to a local computer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://winscp.net"&gt;WinSCP&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.digitalocean.com/community/tutorials/how-to-use-sftp-to-securely-transfer-files-with-a-remote-server"&gt;How To Use SFTP to Securely Transfer Files with a Remote Server&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.digitalocean.com/community/tutorials/how-to-use-filezilla-to-transfer-and-manage-files-securely-on-your-vps"&gt;How To Use Filezilla to Transfer and Manage Files Securely on your VPS&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="step-10-—-installing-the-client-configuration"&gt;Step 10 — Installing the Client Configuration&lt;/h2&gt;

&lt;p&gt;This section covers how to install a client VPN profile on Windows, macOS, Linux, iOS, and Android. None of these client instructions are dependent on one another, so feel free to skip to whichever is applicable to your device.&lt;/p&gt;

&lt;p&gt;The OpenVPN connection will have the same name as whatever you called the &lt;code&gt;.ovpn&lt;/code&gt; file. In regards to this tutorial, this means that the connection is named &lt;code&gt;client1.ovpn&lt;/code&gt;, aligning with the first client file you generated.&lt;/p&gt;

&lt;h3 id="windows"&gt;Windows&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Installing&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Download the OpenVPN client application for Windows from &lt;a href="https://openvpn.net/index.php/open-source/downloads.html"&gt;OpenVPN's Downloads page&lt;/a&gt;. Choose the appropriate installer version for your version of Windows.&lt;/p&gt;

&lt;p&gt;&lt;div class='code-label notes-and-warnings note' title='Note'&gt;Note&lt;/div&gt;&lt;span class='note'&gt;
OpenVPN needs administrative privileges to install.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;After installing OpenVPN, copy the &lt;code&gt;.ovpn&lt;/code&gt; file to:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;C:\Program Files\OpenVPN\config
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When you launch OpenVPN, it will automatically see the profile and makes it available.&lt;/p&gt;

&lt;p&gt;You must run OpenVPN as an administrator each time it's used, even by administrative accounts. To do this without having to right-click and select &lt;strong&gt;Run as administrator&lt;/strong&gt; every time you use the VPN, you must preset this from an administrative account. This also means that standard users will need to enter the administrator's password to use OpenVPN. On the other hand, standard users can't properly connect to the server unless the OpenVPN application on the client has admin rights, so the elevated privileges are necessary.&lt;/p&gt;

&lt;p&gt;To set the OpenVPN application to always run as an administrator, right-click on its shortcut icon and go to &lt;strong&gt;Properties&lt;/strong&gt;. At the bottom of the &lt;strong&gt;Compatibility&lt;/strong&gt; tab, click the button to &lt;strong&gt;Change settings for all users&lt;/strong&gt;. In the new window, check &lt;strong&gt;Run this program as an administrator&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Connecting&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Each time you launch the OpenVPN GUI, Windows will ask if you want to allow the program to make changes to your computer. Click &lt;strong&gt;Yes&lt;/strong&gt;. Launching the OpenVPN client application only puts the applet in the system tray so that you can connect and disconnect the VPN as needed; it does not actually make the VPN connection.&lt;/p&gt;

&lt;p&gt;Once OpenVPN is started, initiate a connection by going into the system tray applet and right-clicking on the OpenVPN applet icon. This opens the context menu. Select &lt;strong&gt;client1&lt;/strong&gt; at the top of the menu (that's your &lt;code&gt;client1.ovpn&lt;/code&gt; profile) and choose &lt;strong&gt;Connect&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A status window will open showing the log output while the connection is established, and a message will show once the client is connected.&lt;/p&gt;

&lt;p&gt;Disconnect from the VPN the same way: Go into the system tray applet, right-click the OpenVPN applet icon, select the client profile and click &lt;strong&gt;Disconnect&lt;/strong&gt;.&lt;/p&gt;

&lt;h3 id="macos"&gt;macOS&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Installing&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://tunnelblick.net/"&gt;Tunnelblick&lt;/a&gt; is a free, open source OpenVPN client for macOS. You can download the latest disk image from the &lt;a href="https://tunnelblick.net/downloads.html"&gt;Tunnelblick Downloads page&lt;/a&gt;. Double-click the downloaded &lt;code&gt;.dmg&lt;/code&gt; file and follow the prompts to install.&lt;/p&gt;

&lt;p&gt;Towards the end of the installation process, Tunnelblick will ask if you have any configuration files. For simplicity, answer &lt;strong&gt;No&lt;/strong&gt; and let Tunnelblick finish. Open a Finder window and double-click &lt;code&gt;client1.ovpn&lt;/code&gt;. Tunnelblick will install the client profile. Administrative privileges are required.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Connecting&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Launch Tunnelblick by double-clicking Tunnelblick in the &lt;strong&gt;Applications&lt;/strong&gt; folder. Once Tunnelblick has been launched, there will be a Tunnelblick icon in the menu bar at the top right of the screen for controlling connections. Click on the icon, and then the &lt;strong&gt;Connect&lt;/strong&gt; menu item to initiate the VPN connection. Select the &lt;strong&gt;client1&lt;/strong&gt; connection.&lt;/p&gt;

&lt;h3 id="linux"&gt;Linux&lt;/h3&gt;

&lt;h4 id="installing"&gt;Installing&lt;/h4&gt;

&lt;p&gt;If you are using Linux, there are a variety of tools that you can use depending on your distribution. Your desktop environment or window manager might also include connection utilities.&lt;/p&gt;

&lt;p&gt;The most universal way of connecting, however, is to just use the OpenVPN software.&lt;/p&gt;

&lt;p&gt;On Ubuntu or Debian, you can install it just as you did on the server by typing:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="client$"&gt;sudo apt update
&lt;/li&gt;&lt;li class="line" prefix="client$"&gt;sudo apt install openvpn
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On CentOS you can enable the EPEL repositories and then install it by typing:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="client$"&gt;sudo yum install epel-release
&lt;/li&gt;&lt;li class="line" prefix="client$"&gt;sudo yum install openvpn
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id="configuring"&gt;Configuring&lt;/h4&gt;

&lt;p&gt;Check to see if your distribution includes an &lt;code&gt;/etc/openvpn/update-resolv-conf&lt;/code&gt; script:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="client1$"&gt;ls /etc/openvpn
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre  local-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;update-resolv-conf
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, edit the OpenVPN client configuration file you transfered:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="client$"&gt;nano &lt;span class="highlight"&gt;client1&lt;/span&gt;.ovpn
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you were able to find an &lt;code&gt;update-resolv-conf&lt;/code&gt; file, uncomment the three lines you added to adjust the DNS settings:&lt;/p&gt;
&lt;div class="code-label " title="client1.ovpn"&gt;client1.ovpn&lt;/div&gt;&lt;pre class="code-pre  local-environment"&gt;&lt;code langs=""&gt;script-security 2
up /etc/openvpn/update-resolv-conf
down /etc/openvpn/update-resolv-conf
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you are using CentOS, change the &lt;code&gt;group&lt;/code&gt; directive from &lt;code&gt;nogroup&lt;/code&gt; to &lt;code&gt;nobody&lt;/code&gt; to match the distribution's available groups:&lt;/p&gt;
&lt;div class="code-label " title="client1.ovpn"&gt;client1.ovpn&lt;/div&gt;&lt;pre class="code-pre  local-environment"&gt;&lt;code langs=""&gt;group &lt;span class="highlight"&gt;nobody&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save and close the file.&lt;/p&gt;

&lt;p&gt;Now, you can connect to the VPN by just pointing the &lt;code&gt;openvpn&lt;/code&gt; command to the client configuration file:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="client$"&gt;sudo openvpn --config &lt;span class="highlight"&gt;client1&lt;/span&gt;.ovpn
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This should connect you to your VPN.&lt;/p&gt;

&lt;h3 id="ios"&gt;iOS&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Installing&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;From the iTunes App Store, search for and install &lt;a href="https://itunes.apple.com/us/app/id590379981"&gt;OpenVPN Connect&lt;/a&gt;, the official iOS OpenVPN client application. To transfer your iOS client configuration onto the device, connect it directly to a computer. &lt;/p&gt;

&lt;p&gt;The process of completing the transfer with iTunes is outlined here. Open iTunes on the computer and click on &lt;strong&gt;iPhone&lt;/strong&gt; &amp;gt; &lt;strong&gt;apps&lt;/strong&gt;. Scroll down to the bottom to the &lt;strong&gt;File Sharing&lt;/strong&gt; section and click the OpenVPN app. The blank window to the right, &lt;strong&gt;OpenVPN Documents&lt;/strong&gt;, is for sharing files. Drag the &lt;code&gt;.ovpn&lt;/code&gt; file to the OpenVPN Documents window.&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/openvpn_ubunutu/1.png" alt="iTunes showing the VPN profile ready to load on the iPhone"&gt;&lt;/p&gt;

&lt;p&gt;Now launch the OpenVPN app on the iPhone. You will receive a notification that a new profile is ready to import. Tap the green plus sign to import it.&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/openvpn_ubunutu/2.png" alt="The OpenVPN iOS app showing new profile ready to import"&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Connecting&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;OpenVPN is now ready to use with the new profile. Start the connection by sliding the &lt;strong&gt;Connect&lt;/strong&gt; button to the &lt;strong&gt;On&lt;/strong&gt; position. Disconnect by sliding the same button to &lt;strong&gt;Off&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;div class='code-label notes-and-warnings note' title='Note'&gt;Note&lt;/div&gt;&lt;span class='note'&gt;
The VPN switch under &lt;strong&gt;Settings&lt;/strong&gt; cannot be used to connect to the VPN. If you try, you will receive a notice to only connect using the OpenVPN app.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/openvpn_ubunutu/3.png" alt="The OpenVPN iOS app connected to the VPN"&gt;&lt;/p&gt;

&lt;h3 id="android"&gt;Android&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Installing&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Open the Google Play Store. Search for and install &lt;a href="https://play.google.com/store/apps/details?id=net.openvpn.openvpn"&gt;Android OpenVPN Connect&lt;/a&gt;, the official Android OpenVPN client application.&lt;/p&gt;

&lt;p&gt;You can transfer the &lt;code&gt;.ovpn&lt;/code&gt; profile by connecting the Android device to your computer by USB and copying the file over. Alternatively, if you have an SD card reader, you can remove the device's SD card, copy the profile onto it and then insert the card back into the Android device. &lt;/p&gt;

&lt;p&gt;Start the OpenVPN app and tap the menu to import the profile.&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/openvpn_ubunutu/4.png" alt="The OpenVPN Android app profile import menu selection"&gt;&lt;/p&gt;

&lt;p&gt;Then navigate to the location of the saved profile (the screenshot uses &lt;code&gt;/sdcard/Download/&lt;/code&gt;) and select the file. The app will make a note that the profile was imported.&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/openvpn_ubunutu/5.png" alt="The OpenVPN Android app selecting VPN profile to import"&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Connecting&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To connect, simply tap the &lt;strong&gt;Connect&lt;/strong&gt; button. You'll be asked if you trust the OpenVPN application. Choose &lt;strong&gt;OK&lt;/strong&gt; to initiate the connection. To disconnect from the VPN, go back to the OpenVPN app and choose &lt;strong&gt;Disconnect&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/openvpn_ubunutu/6.png" alt="The OpenVPN Android app ready to connect to the VPN"&gt;&lt;/p&gt;

&lt;h2 id="step-11-—-testing-your-vpn-connection-optional"&gt;Step 11 — Testing Your VPN Connection (Optional)&lt;/h2&gt;

&lt;p&gt;&lt;span class='note'&gt;&lt;strong&gt;Note:&lt;/strong&gt; This method for testing your VPN connection will only work if you opted to route all your traffic through the VPN in Step 5.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;Once everything is installed, a simple check confirms everything is working properly. Without having a VPN connection enabled, open a browser and go to &lt;a href="https://www.dnsleaktest.com"&gt;DNSLeakTest&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The site will return the IP address assigned by your internet service provider and as you appear to the rest of the world. To check your DNS settings through the same website, click on &lt;strong&gt;Extended Test&lt;/strong&gt; and it will tell you which DNS servers you are using.&lt;/p&gt;

&lt;p&gt;Now connect the OpenVPN client to your server’s VPN and refresh the browser. A completely different IP address (that of your VPN server) should now appear, and this is how you appear to the world. Again, &lt;a href="https://www.dnsleaktest.com"&gt;DNSLeakTest's&lt;/a&gt; &lt;strong&gt;Extended Test&lt;/strong&gt; will check your DNS settings and confirm you are now using the DNS resolvers pushed by your VPN.&lt;/p&gt;

&lt;h2 id="step-12-—-revoking-client-certificates"&gt;Step 12 — Revoking Client Certificates&lt;/h2&gt;

&lt;p&gt;Occasionally, you may need to revoke a client certificate to prevent further access to the OpenVPN server.&lt;/p&gt;

&lt;p&gt;To do so, navigate to the EasyRSA directory on your CA machine:&lt;/p&gt;
&lt;pre class="code-pre command second-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cd EasyRSA-&lt;span class="highlight"&gt;3.0.4&lt;/span&gt;/
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, run the &lt;code&gt;easyrsa&lt;/code&gt; script with the &lt;code&gt;revoke&lt;/code&gt; option, followed by the client name you wish to revoke:&lt;/p&gt;
&lt;pre class="code-pre command second-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;./easyrsa revoke &lt;span class="highlight"&gt;client2&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will ask you to confirm the revocation by entering &lt;code&gt;yes&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre  second-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Please confirm you wish to revoke the certificate with the following subject:

subject=
    commonName                = client2


Type the word 'yes' to continue, or any other input to abort.
  Continue with revocation: &lt;span class="highlight"&gt;yes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After confirming the action, the CA will fully revoke the client’s certificate. However, your OpenVPN server currently has no way to check whether any clients’ certificates have been revoked and the client will still have access to the VPN. To correct this, create a certificate revocation list (CRL) on your CA machine:&lt;/p&gt;
&lt;pre class="code-pre command second-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;./easyrsa gen-crl
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will generate a file called &lt;code&gt;crl.pem&lt;/code&gt;. Securely transfer this file to your OpenVPN server:&lt;/p&gt;
&lt;pre class="code-pre command second-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;scp ~/EasyRSA-&lt;span class="highlight"&gt;3.0.4&lt;/span&gt;/pki/crl.pem &lt;span class="highlight"&gt;sammy&lt;/span&gt;@&lt;span class="highlight"&gt;your_server_ip&lt;/span&gt;:/tmp
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On your OpenVPN server, copy this file into your &lt;code&gt;/etc/openvpn/&lt;/code&gt; directory:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo cp /tmp/crl.pem /etc/openvpn
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, open the OpenVPN server configuration file:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/openvpn/server.conf
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At the bottom of the file, add the &lt;code&gt;crl-verify&lt;/code&gt; option, which will instruct the OpenVPN server to check the certificate revocation list that we've created each time a connection attempt is made:&lt;/p&gt;
&lt;div class="code-label " title="/etc/openvpn/server.conf"&gt;/etc/openvpn/server.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;crl-verify crl.pem
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save and close the file.&lt;/p&gt;

&lt;p&gt;Finally, restart OpenVPN to implement the certificate revocation:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl restart openvpn@server
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The client should no longer be able to successfully connect to the server using the old credential.&lt;/p&gt;

&lt;p&gt;To revoke additional clients, follow this process:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Revoke the certificate with the &lt;code&gt;./easyrsa revoke &lt;span class="highlight"&gt;client_name&lt;/span&gt;&lt;/code&gt; command&lt;/li&gt;
&lt;li&gt;Generate a new CRL&lt;/li&gt;
&lt;li&gt;Transfer the new &lt;code&gt;crl.pem&lt;/code&gt; file to your OpenVPN server and copy it to the &lt;code&gt;/etc/openvpn&lt;/code&gt; directory to overwrite the old list.&lt;/li&gt;
&lt;li&gt;Restart the OpenVPN service.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can use this process to revoke any certificates that you've previously issued for your server.&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;You are now securely traversing the internet protecting your identity, location, and traffic from snoopers and censors. If at this point you no longer need to issue certificates, it's recommended that you turn off your CA machine or otherwise disconnect it from the internet until you need to add or revoke certificates. This will help to prevent attackers from gaining access to your VPN.&lt;/p&gt;

&lt;p&gt;To configure more clients, you only need to follow steps &lt;strong&gt;4&lt;/strong&gt; and &lt;strong&gt;9-11&lt;/strong&gt; for each additional device. To revoke access to clients, just follow step &lt;strong&gt;12&lt;/strong&gt;.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-install-and-configure-nextcloud-on-debian-9</id>
    <published>2018-09-07T21:00:36Z</published>
    <updated>2018-09-07T21:00:49Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-install-and-configure-nextcloud-on-debian-9"/>
    <title>How To Install and Configure Nextcloud on Debian 9</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://nextcloud.com/"&gt;Nextcloud&lt;/a&gt;, a fork of ownCloud, is a file sharing server that permits you to store your personal content, like documents and pictures, in a centralized location, much like Dropbox.  The difference with Nextcloud is that all of its features are open-source.  It also returns the control and security of your sensitive data back to you, thus eliminating the use of a third-party cloud hosting service.&lt;/p&gt;

&lt;p&gt;In this tutorial, we will install and configure a Nextcloud instance on a Debian 9 server.&lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;In order to complete the steps in this guide, you will need the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;A sudo user and firewall configured on your server&lt;/strong&gt;: You can create a user with &lt;code&gt;sudo&lt;/code&gt; privileges and set up a basic firewall by following the &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-debian-9"&gt;Debian 9 initial server setup guide&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;(Optional) A domain name pointed to your server&lt;/strong&gt;:  We will be securing connections to the Nextcloud installation with TLS/SSL.  Nextcloud can set up and manage a free, trusted SSL certificate from &lt;a href="https://letsencrypt.org/"&gt;Let's Encrypt&lt;/a&gt; if your server has a domain name.  If not, Nextcloud can set up a self-signed SSL certificate that can encrypt connections, but won't be trusted by default in web browsers.  If you are using DigitalOcean, you can follow our guide on &lt;a href="https://www.digitalocean.com/docs/networking/dns/how-to/add-domains/"&gt;how to set up a domain name for your server&lt;/a&gt; if you intend to use Let's Encrypt.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you have completed the above steps, continue on to learn how to set up Nextcloud on your server.&lt;/p&gt;

&lt;h2 id="step-1-–-installing-nextcloud"&gt;Step 1 – Installing Nextcloud&lt;/h2&gt;

&lt;p&gt;We will be installing Nextcloud using the &lt;a href="https://en.wikipedia.org/wiki/Snappy_(package_manager)"&gt;snappy&lt;/a&gt; packaging system.  This packaging system, installable on Debian 9 through the default repositories, allows organizations to ship software, along with all associated dependencies and configuration, in a self-contained unit with automatic updates.  This means that instead of installing and configuring a web and database server and then configuring the Nextcloud app to run on it, we can install the &lt;code&gt;snap&lt;/code&gt; package which handles the underlying systems automatically.&lt;/p&gt;

&lt;p&gt;To install and manage &lt;code&gt;snap&lt;/code&gt; packages, we first need to install the &lt;code&gt;snapd&lt;/code&gt; package on the server.  Update the local package index for &lt;code&gt;apt&lt;/code&gt; and then install the software by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt update
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo apt install snapd
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, either log out and log back in again, or source the &lt;code&gt;/etc/profile.d/apps-bin-path.sh&lt;/code&gt; script to add &lt;code&gt;/snap/bin&lt;/code&gt; to your session's &lt;code&gt;PATH&lt;/code&gt; variable:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;source /etc/profile.d/apps-bin-path.sh
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once &lt;code&gt;snapd&lt;/code&gt; is installed, you can download the Nextcloud &lt;code&gt;snap&lt;/code&gt; package and install it on the system by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo snap install nextcloud
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The Nextcloud package will be downloaded and installed on your server.  You can confirm that the installation process was successful by listing the changes associated with the &lt;code&gt;snap&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;snap changes nextcloud
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;ID   Status  Spawn               Ready               Summary
1    &lt;span class="highlight"&gt;Done&lt;/span&gt;    today at 20:18 UTC  today at 20:18 UTC  &lt;span class="highlight"&gt;Install "nextcloud" snap&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The status and summary indicate that the installation was completed without any problems.&lt;/p&gt;

&lt;h3 id="getting-additional-information-about-the-nextcloud-snap"&gt;Getting Additional Information About the Nextcloud Snap&lt;/h3&gt;

&lt;p&gt;If you'd like some more information about the Nextcloud &lt;code&gt;snap&lt;/code&gt;, there are a few commands that can be helpful.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;snap info&lt;/code&gt; command can show you the description, the Nextcloud management commands available, as well as the installed version and the snap channel being tracked:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;snap info nextcloud
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Snaps can define interfaces they support, which consist of a slot and plug that, when hooked together, gives the snap access to certain capabilities or levels of access.  For instance, snaps that need to act as a network client must have the &lt;code&gt;network&lt;/code&gt; interface.  To see what snap "interfaces" this snap defines, type:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;snap interfaces nextcloud
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Slot           Plug
:network       nextcloud
:network-bind  nextcloud
-              nextcloud:removable-media
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To learn about all of the specific services and apps that this snap provides, you can take a look at the snap definition file by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;less /snap/nextcloud/current/meta/snap.yaml
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will allow you to see the individual components included within the snap, if you need help with debugging.&lt;/p&gt;

&lt;h2 id="configuring-an-administrative-account"&gt;Configuring an Administrative Account&lt;/h2&gt;

&lt;p&gt;There are a few different ways you can configure the Nextcloud snap.  In this guide, rather than creating an administrative user through the web interface, we will create one on the command line in order to avoid a small window where the administrator registration page would be accessible to anyone visiting your server's IP address or domain name.&lt;/p&gt;

&lt;p&gt;To configure Nextcloud with a new administrator account, use the &lt;code&gt;nextcloud.manual-install&lt;/code&gt; command.  You must pass in a username and a password as arguments:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo -i nextcloud.manual-install &lt;span class="highlight"&gt;sammy&lt;/span&gt; &lt;span class="highlight"&gt;password&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The following message indicates that Nextcloud has been configured correctly:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Nextcloud is not installed - only a limited number of commands are available
&lt;span class="highlight"&gt;Nextcloud was successfully installed&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that Nextcloud is installed, we need to adjust the trusted domains so that Nextcloud will respond to requests using the server's domain name or IP address.&lt;/p&gt;

&lt;h2 id="adjusting-the-trusted-domains"&gt;Adjusting the Trusted Domains&lt;/h2&gt;

&lt;p&gt;When installing from the command line, Nextcloud restricts the host names that the instance will respond to.  By default, the service only responds to requests made to the "localhost" hostname.  We will be accessing Nextcloud through the server's domain name or IP address, so we'll need to adjust this setting to accept these type of requests.&lt;/p&gt;

&lt;p&gt;You can view the current settings by querying the value of the &lt;code&gt;trusted_domains&lt;/code&gt; array:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo -i nextcloud.occ config:system:get trusted_domains
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;&lt;span class="highlight"&gt;localhost&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Currently, only &lt;code&gt;localhost&lt;/code&gt; is present as the first value in the array.  We can add an entry for our server's domain name or IP address by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo -i nextcloud.occ config:system:set trusted_domains 1 --value=&lt;span class="highlight"&gt;example.com&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;&lt;span class="highlight"&gt;System config value trusted_domains =&amp;gt; 1 set to string example.com&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If we query the trusted domains again, we will see that we now have two entries:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo -i nextcloud.occ config:system:get trusted_domains
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;&lt;span class="highlight"&gt;localhost&lt;/span&gt;
&lt;span class="highlight"&gt;example.com&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you need to add another way of accessing the Nextcloud instance, you can add additional domains or addresses by rerunning the &lt;code&gt;config:system:set&lt;/code&gt; command with an incremented index number (the "1" in the first command) and adjusting the &lt;code&gt;--value&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id="securing-the-nextcloud-web-interface-with-ssl"&gt;Securing the Nextcloud Web Interface with SSL&lt;/h2&gt;

&lt;p&gt;Before we begin using Nextcloud, we need to secure the web interface.&lt;/p&gt;

&lt;p&gt;If you have a domain name associated with your Nextcloud server, the Nextcloud snap can help you obtain and configure a trusted SSL certificate from &lt;a href="https://letsencrypt.org/"&gt;Let's Encrypt&lt;/a&gt;.  If your Nextcloud server &lt;em&gt;does not&lt;/em&gt; have a domain name, Nextcloud can configure a self-signed certificate which will encrypt your web traffic but won't be able to verify the identity of your server.&lt;/p&gt;

&lt;p&gt;With that in mind, follow the section below that matches your scenario.&lt;/p&gt;

&lt;h3 id="option-1-setting-up-ssl-with-let-39-s-encrypt"&gt;Option 1: Setting Up SSL with Let's Encrypt&lt;/h3&gt;

&lt;p&gt;If you have a domain name associated with your Nextcloud server, the best option for securing your web interface is to obtain a Let's Encrypt SSL certificate.&lt;/p&gt;

&lt;p&gt;Start by opening the ports in the firewall that Let's Encrypt uses to validate domain ownership.  This will make your Nextcloud login page publicly accessible, but since we already have an administrator account configured, no one will be able to hijack the installation:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw allow "WWW Full"
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, request a Let's Encrypt certificate by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo -i nextcloud.enable-https lets-encrypt
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will first be asked whether your server meets the conditions necessary to request a certificate from the Let's Encrypt service:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;In order for Let's Encrypt to verify that you actually own the
domain(s) for which you're requesting a certificate, there are a
number of requirements of which you need to be aware:

1. In order to register with the Let's Encrypt ACME server, you must
   agree to the currently-in-effect Subscriber Agreement located
   here:

       https://letsencrypt.org/repository/

   By continuing to use this tool you agree to these terms. Please
   cancel now if otherwise.

2. You must have the domain name(s) for which you want certificates
   pointing at the external IP address of this machine.

3. Both ports 80 and 443 on the external IP address of this machine
   must point to this machine (e.g. port forwarding might need to be
   setup on your router).

Have you met these requirements? (y/n)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Type &lt;strong&gt;y&lt;/strong&gt; to continue.&lt;/p&gt;

&lt;p&gt;Next, you will be asked to provide an email address to use for recovery operations:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Please enter an email address (for urgent notices or key recovery): &lt;span class="highlight"&gt;your_email@domain.com&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, enter the domain name associated with your Nextcloud server:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Please enter your domain name(s) (space-separated): &lt;span class="highlight"&gt;example.com&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Your Let's Encrypt certificate will be requested and, provided everything went well, the internal Apache instance will be restarted to immediately implement SSL:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Attempting to obtain certificates... done
Restarting apache... done
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can now skip ahead to &lt;a href="#logging-in-to-the-nextcloud-web-interface"&gt;sign into Nextcloud for the first time&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id="option-2-setting-up-ssl-with-a-self-signed-certificate"&gt;Option 2: Setting Up SSL with a Self-Signed Certificate&lt;/h3&gt;

&lt;p&gt;If your Nextcloud server &lt;em&gt;does not&lt;/em&gt; have a domain name, you can still secure the web interface by generating a self-signed SSL certificate.  This certificate will allow access to the web interface over an encrypted connection, but will be unable to verify the identity of your server, so your browser will likely display a warning.&lt;/p&gt;

&lt;p&gt;To generate a self-signed certificate and configure Nextcloud to use it, type:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nextcloud.enable-https self-signed
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Generating key and self-signed certificate... done
Restarting apache... done
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The above output indicates that Nextcloud generated and enabled a self-signed certificate.&lt;/p&gt;

&lt;p&gt;Now that the interface is secure, open the web ports in the firewall to allow access to the web interface:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw allow "WWW Full"
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You are now ready to log into Nextcloud for the first time.&lt;/p&gt;

&lt;h2 id="logging-in-to-the-nextcloud-web-interface"&gt;Logging in to the Nextcloud Web Interface&lt;/h2&gt;

&lt;p&gt;Now that Nextcloud is configured, visit your server's domain name or IP address in your web browser:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;https://&lt;span class="highlight"&gt;example.com&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span class='note'&gt;&lt;strong&gt;Note:&lt;/strong&gt; If you set up a self-signed SSL certificate, your browser may display a warning that the connection is insecure because the server's certificate is not signed by a recognized certificate authority.  This is expected for self-signed certificates, so feel free to click through the warning to proceed to the site.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;Since you have already configure an administrator account from the command line, you will be taken to the Nextcloud login page.  Enter the credentials you created for the administrative user:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/nextcloud_1804/login_page.png" alt="Nextcloud login page"&gt;&lt;/p&gt;

&lt;p&gt;Click the &lt;strong&gt;Log in&lt;/strong&gt; button to log in to the Nextcloud web interface.&lt;/p&gt;

&lt;p&gt;The first time you enter, a window will be displayed with links to various Nextcloud clients that can be used to interact with and manage your Nextcloud instance:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/nextcloud_1804/modal.png" alt="Nextcloud client modal"&gt;&lt;/p&gt;

&lt;p&gt;Click through to download any clients you are interested in, or exit out of the window by clicking the &lt;strong&gt;X&lt;/strong&gt; in the upper-right corner.  You will be taken to the main Nextcloud interface, where you can begin to upload and manage files:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/nextcloud_1804/main_page.png" alt="Nextcloud main page"&gt;&lt;/p&gt;

&lt;p&gt;Your installation is now complete and secured.  Feel free to explore the interface to get more familiarity with the features and functionality of your new system.&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Nextcloud can replicate the capabilities of popular third-party cloud storage services. Content can be shared between users or externally with public URLs. The advantage of Nextcloud is that the information is stored securely in a place that you control.&lt;/p&gt;

&lt;p&gt;Explore the interface and for additional functionality, install plugins using &lt;a href="https://apps.nextcloud.com/"&gt;Nextcloud's app store&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-create-a-self-signed-ssl-certificate-for-nginx-on-debian-9</id>
    <published>2018-09-07T19:59:36Z</published>
    <updated>2018-09-07T20:00:50Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-create-a-self-signed-ssl-certificate-for-nginx-on-debian-9"/>
    <title>How To Create a Self-Signed SSL Certificate for Nginx on Debian 9</title>
    <content type="html">&lt;p&gt;&lt;em&gt;A previous version of this tutorial was written by &lt;a href="https://www.digitalocean.com/community/users/jellingwood"&gt;Justin Ellingwood&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;TLS&lt;/strong&gt;, or transport layer security, and its predecessor &lt;strong&gt;SSL&lt;/strong&gt;, which stands for secure sockets layer, are web protocols used to wrap normal traffic in a protected, encrypted wrapper.&lt;/p&gt;

&lt;p&gt;Using this technology, servers can send traffic safely between the server and clients without the possibility of the messages being intercepted by outside parties. The certificate system also assists users in verifying the identity of the sites that they are connecting with.&lt;/p&gt;

&lt;p&gt;In this guide, we will show you how to set up a self-signed SSL certificate for use with an Nginx web server on a Debian 9 server.&lt;/p&gt;

&lt;span class='note'&gt;&lt;p&gt;
&lt;strong&gt;Note:&lt;/strong&gt; A self-signed certificate will encrypt communication between your server and any clients. However, because it is not signed by any of the trusted certificate authorities included with web browsers, users cannot use the certificate to validate the identity of your server automatically.&lt;/p&gt;

&lt;p&gt;A self-signed certificate may be appropriate if you do not have a domain name associated with your server and for instances where the encrypted web interface is not user-facing. If you &lt;em&gt;do&lt;/em&gt; have a domain name, in many cases it is better to use a CA-signed certificate. To learn how to set up a free trusted certificate with the Let's Encrypt project, consult &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-secure-nginx-with-let-s-encrypt-on-debian-9"&gt;How to Secure Nginx with Let’s Encrypt on Debian 9&lt;/a&gt;.&lt;br&gt;&lt;/p&gt;&lt;/span&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;Before you begin, you should have a non-root user configured with &lt;code&gt;sudo&lt;/code&gt; privileges. You can learn how to set up such a user account by following our &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-debian-9"&gt;initial server setup for Debian 9&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You will also need to have the Nginx web server installed. If you would like to install an entire LEMP (Linux, Nginx, MySQL, PHP) stack on your server, you can follow our guide on &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-linux-nginx-mysql-php-lemp-stack-debian-9"&gt;setting up LEMP on Debian 9&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you just want the Nginx web server, you can instead follow our guide on &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-nginx-on-debian-9"&gt;installing Nginx on Debian 9&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;When you have completed the prerequisites, continue below.&lt;/p&gt;

&lt;h2 id="step-1-—-creating-the-ssl-certificate"&gt;Step 1 — Creating the SSL Certificate&lt;/h2&gt;

&lt;p&gt;TLS/SSL works by using a combination of a public certificate and a private key. The SSL key is kept secret on the server. It is used to encrypt content sent to clients. The SSL certificate is publicly shared with anyone requesting the content. It can be used to decrypt the content signed by the associated SSL key.&lt;/p&gt;

&lt;p&gt;We can create a self-signed key and certificate pair with OpenSSL in a single command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/ssl/private/nginx-selfsigned.key -out /etc/ssl/certs/nginx-selfsigned.crt
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will be asked a series of questions. Before we go over that, let's take a look at what is happening in the command we are issuing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;openssl&lt;/strong&gt;: This is the basic command line tool for creating and managing OpenSSL certificates, keys, and other files.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;req&lt;/strong&gt;: This subcommand specifies that we want to use X.509 certificate signing request (CSR) management. The "X.509" is a public key infrastructure standard that SSL and TLS adheres to for its key and certificate management. We want to create a new X.509 cert, so we are using this subcommand.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;-x509&lt;/strong&gt;: This further modifies the previous subcommand by telling the utility that we want to make a self-signed certificate instead of generating a certificate signing request, as would normally happen.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;-nodes&lt;/strong&gt;: This tells OpenSSL to skip the option to secure our certificate with a passphrase. We need Nginx to be able to read the file, without user intervention, when the server starts up. A passphrase would prevent this from happening because we would have to enter it after every restart.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;-days 365&lt;/strong&gt;: This option sets the length of time that the certificate will be considered valid. We set it for one year here.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;-newkey rsa:2048&lt;/strong&gt;: This specifies that we want to generate a new certificate and a new key at the same time. We did not create the key that is required to sign the certificate in a previous step, so we need to create it along with the certificate. The &lt;code&gt;rsa:2048&lt;/code&gt; portion tells it to make an RSA key that is 2048 bits long.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;-keyout&lt;/strong&gt;: This line tells OpenSSL where to place the generated private key file that we are creating.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;-out&lt;/strong&gt;: This tells OpenSSL where to place the certificate that we are creating.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As we stated above, these options will create both a key file and a certificate. We will be asked a few questions about our server in order to embed the information correctly in the certificate.&lt;/p&gt;

&lt;p&gt;Fill out the prompts appropriately. &lt;strong&gt;The most important line is the one that requests the &lt;code&gt;Common Name (e.g. server FQDN or YOUR name)&lt;/code&gt;. You need to enter the domain name associated with your server or, more likely, your server's public IP address.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The entirety of the prompts will look something like this:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Country Name (2 letter code) [AU]:&lt;span class="highlight"&gt;US&lt;/span&gt;
State or Province Name (full name) [Some-State]:&lt;span class="highlight"&gt;New York&lt;/span&gt;
Locality Name (eg, city) []:&lt;span class="highlight"&gt;New York City&lt;/span&gt;
Organization Name (eg, company) [Internet Widgits Pty Ltd]:&lt;span class="highlight"&gt;Bouncy Castles, Inc.&lt;/span&gt;
Organizational Unit Name (eg, section) []:&lt;span class="highlight"&gt;Ministry of Water Slides&lt;/span&gt;
Common Name (e.g. server FQDN or YOUR name) []:&lt;span class="highlight"&gt;server_IP_address&lt;/span&gt;
Email Address []:&lt;span class="highlight"&gt;admin@your_domain.com&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Both of the files you created will be placed in the appropriate subdirectories of the &lt;code&gt;/etc/ssl&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;While we are using OpenSSL, we should also create a strong Diffie-Hellman group, which is used in negotiating &lt;a href="https://en.wikipedia.org/wiki/Forward_secrecy"&gt;Perfect Forward Secrecy&lt;/a&gt; with clients.&lt;/p&gt;

&lt;p&gt;We can do this by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo openssl dhparam -out /etc/nginx/dhparam.pem 4096
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will take a while, but when it's done you will have a strong DH group at &lt;code&gt;/etc/nginx/dhparam.pem&lt;/code&gt; that we can use in our configuration.&lt;/p&gt;

&lt;h2 id="step-2-—-configuring-nginx-to-use-ssl"&gt;Step 2 — Configuring Nginx to Use SSL&lt;/h2&gt;

&lt;p&gt;We have created our key and certificate files under the &lt;code&gt;/etc/ssl&lt;/code&gt; directory. Now we just need to modify our Nginx configuration to take advantage of these.&lt;/p&gt;

&lt;p&gt;We will make a few adjustments to our configuration.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; We will create a configuration snippet containing our SSL key and certificate file locations.&lt;/li&gt;
&lt;li&gt; We will create a configuration snippet containing strong SSL settings that can be used with any certificates in the future.&lt;/li&gt;
&lt;li&gt; We will adjust our Nginx server blocks to handle SSL requests and use the two snippets above.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This method of configuring Nginx will allow us to keep clean server blocks and put common configuration segments into reusable modules.&lt;/p&gt;

&lt;h3 id="creating-a-configuration-snippet-pointing-to-the-ssl-key-and-certificate"&gt;Creating a Configuration Snippet Pointing to the SSL Key and Certificate&lt;/h3&gt;

&lt;p&gt;First, let's create a new Nginx configuration snippet in the &lt;code&gt;/etc/nginx/snippets&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;To properly distinguish the purpose of this file, let's call it &lt;code&gt;self-signed.conf&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/nginx/snippets/self-signed.conf
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Within this file, we need to set the &lt;code&gt;ssl_certificate&lt;/code&gt; directive to our certificate file and the &lt;code&gt;ssl_certificate_key&lt;/code&gt; to the associated key. In our case, this will look like this:&lt;/p&gt;
&lt;div class="code-label " title="/etc/nginx/snippets/self-signed.conf"&gt;/etc/nginx/snippets/self-signed.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;ssl_certificate /etc/ssl/certs/nginx-selfsigned.crt;
ssl_certificate_key /etc/ssl/private/nginx-selfsigned.key;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When you've added those lines, save and close the file.&lt;/p&gt;

&lt;h3 id="creating-a-configuration-snippet-with-strong-encryption-settings"&gt;Creating a Configuration Snippet with Strong Encryption Settings&lt;/h3&gt;

&lt;p&gt;Next, we will create another snippet that will define some SSL settings. This will set Nginx up with a strong SSL cipher suite and enable some advanced features that will help keep our server secure.&lt;/p&gt;

&lt;p&gt;The parameters we will set can be reused in future Nginx configurations, so we will give the file a generic name:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/nginx/snippets/ssl-params.conf
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To set up Nginx SSL securely, we will be using the recommendations by Remy van Elst on the &lt;a href="https://cipherli.st"&gt;Cipherli.st&lt;/a&gt; site. This site is designed to provide easy-to-consume encryption settings for popular software.&lt;/p&gt;

&lt;span class='note'&gt;&lt;p&gt;
The suggested settings on the site linked to above offer strong security. Sometimes, this comes at the cost of greater client compatibility. If you need to support older clients, there is an alternative list that can be accessed by clicking the link on the page labelled "Yes, give me a ciphersuite that works with legacy / old software." That list can be substituted for the items copied below.&lt;/p&gt;

&lt;p&gt;The choice of which config you use will depend largely on what you need to support. They both will provide great security.&lt;br&gt;&lt;/p&gt;&lt;/span&gt;

&lt;p&gt;For our purposes, we can copy the provided settings in their entirety. We just need to make a few small modifications.&lt;/p&gt;

&lt;p&gt;First, we will add our preferred DNS resolver for upstream requests. We will use Google's for this guide.&lt;/p&gt;

&lt;p&gt;Second, we will comment out the line that sets the strict transport security header. Before uncommenting this line, you should take take a moment to read up on &lt;a href="https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security"&gt;HTTP Strict Transport Security, or HSTS&lt;/a&gt;, specifically about the &lt;a href="https://hstspreload.appspot.com/"&gt;"preload" functionality&lt;/a&gt;. Preloading HSTS provides increased security, but can have far reaching consequences if accidentally enabled or enabled incorrectly.&lt;/p&gt;

&lt;p&gt;Copy the following into your &lt;code&gt;ssl-params.conf&lt;/code&gt; snippet file:&lt;/p&gt;
&lt;div class="code-label " title="/etc/nginx/snippets/ssl-params.conf"&gt;/etc/nginx/snippets/ssl-params.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;ssl_protocols TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_dhparam /etc/nginx/dhparam.pem;
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384;
ssl_ecdh_curve secp384r1; # Requires nginx &amp;gt;= 1.1.0
ssl_session_timeout  10m;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off; # Requires nginx &amp;gt;= 1.5.9
ssl_stapling on; # Requires nginx &amp;gt;= 1.3.7
ssl_stapling_verify on; # Requires nginx =&amp;gt; 1.3.7
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
# Disable strict transport security for now. You can uncomment the following
# line if you understand the implications.
# add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload";
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Because we are using a self-signed certificate, SSL stapling will not be used. Nginx will output a warning but continue to operate correctly.&lt;/p&gt;

&lt;p&gt;Save and close the file when you are finished.&lt;/p&gt;

&lt;h3 id="adjusting-the-nginx-configuration-to-use-ssl"&gt;Adjusting the Nginx Configuration to Use SSL&lt;/h3&gt;

&lt;p&gt;Now that we have our snippets, we can adjust our Nginx configuration to enable SSL.&lt;/p&gt;

&lt;p&gt;We will assume in this guide that you are using a custom server block configuration file in the &lt;code&gt;/etc/nginx/sites-available&lt;/code&gt; directory. We will use &lt;code&gt;/etc/nginx/sites-available/example.com&lt;/code&gt; for this example. Substitute your configuration filename as needed.&lt;/p&gt;

&lt;p&gt;Before we go any further, let's back up our current configuration file:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo cp /etc/nginx/sites-available/&lt;span class="highlight"&gt;example.com&lt;/span&gt; /etc/nginx/sites-available/&lt;span class="highlight"&gt;example.com&lt;/span&gt;.bak
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, open the configuration file to make adjustments:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/nginx/sites-available/&lt;span class="highlight"&gt;example.com&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Inside, your server block probably begins similar to this:&lt;/p&gt;
&lt;div class="code-label " title="/etc/nginx/sites-available/example.com"&gt;/etc/nginx/sites-available/example.com&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;server {
    listen 80;
    listen [::]:80;

    server_name &lt;span class="highlight"&gt;example.com www.example.com&lt;/span&gt;;

    root /var/www/example.com/html;
    index index.html index.htm index.nginx-debian.html;

    . . .
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Your file may be in a different order, and instead of the &lt;code&gt;root&lt;/code&gt; and &lt;code&gt;index&lt;/code&gt; directives you may have some &lt;code&gt;location&lt;/code&gt;, &lt;code&gt;proxy_pass&lt;/code&gt;, or other custom configuration statements. This is ok, as we only need to update the &lt;code&gt;listen&lt;/code&gt; directives and include our SSL snippets. We will be modifying this existing server block to serve SSL traffic on port 443, then create a new server block to respond on port 80 and automatically redirect traffic to port 443.&lt;/p&gt;

&lt;p&gt;&lt;span class='note'&gt;&lt;strong&gt;Note:&lt;/strong&gt; We will use a 302 redirect until we have verified that everything is working properly. Afterwards, we can change this to a permanent 301 redirect.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;In your existing configuration file, update the two &lt;code&gt;listen&lt;/code&gt; statements to use port 443 and SSL, then include the two snippet files we created in previous steps:&lt;/p&gt;
&lt;div class="code-label " title="/etc/nginx/sites-available/example.com"&gt;/etc/nginx/sites-available/example.com&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;server {
    listen &lt;span class="highlight"&gt;443 ssl&lt;/span&gt;;
    listen [::]:&lt;span class="highlight"&gt;443 ssl&lt;/span&gt;;
    &lt;span class="highlight"&gt;include snippets/self-signed.conf;&lt;/span&gt;
    &lt;span class="highlight"&gt;include snippets/ssl-params.conf;&lt;/span&gt;

    server_name &lt;span class="highlight"&gt;example.com www.example.com&lt;/span&gt;;

    root /var/www/example.com/html;
    index index.html index.htm index.nginx-debian.html;

    . . .
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, paste a second server block into the configuration file, after the closing bracket (&lt;code&gt;}&lt;/code&gt;) of the first block:&lt;/p&gt;
&lt;div class="code-label " title="/etc/nginx/sites-available/example.com"&gt;/etc/nginx/sites-available/example.com&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .
server {
    listen 80;
    listen [::]:80;

    server_name &lt;span class="highlight"&gt;example.com www.example.com&lt;/span&gt;;

    return 302 https://$server_name$request_uri;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is a bare-bones configuration that listens on port 80 and performs the redirect to HTTPS. Save and close the file when you are finished editing it.&lt;/p&gt;

&lt;h2 id="step-3-—-adjusting-the-firewall"&gt;Step 3 — Adjusting the Firewall&lt;/h2&gt;

&lt;p&gt;If you have the &lt;code&gt;ufw&lt;/code&gt; firewall enabled, as recommended by the prerequisite guides, you'll need to adjust the settings to allow for SSL traffic. Luckily, Nginx registers a few profiles with &lt;code&gt;ufw&lt;/code&gt; upon installation.&lt;/p&gt;

&lt;p&gt;We can see the available profiles by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw app list
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see a list like this:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Available applications:
. . .
  Nginx Full
  Nginx HTTP
  Nginx HTTPS
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can see the current setting by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw status
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It will probably look like this, meaning that only HTTP traffic is allowed to the web server:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Status: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere
Nginx HTTP                 ALLOW       Anywhere
OpenSSH (v6)               ALLOW       Anywhere (v6)
Nginx HTTP (v6)            ALLOW       Anywhere (v6)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To additionally let in HTTPS traffic, we can allow the "Nginx Full" profile and then delete the redundant "Nginx HTTP" profile allowance:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw allow 'Nginx Full'
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo ufw delete allow 'Nginx HTTP'
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Your status should look like this now:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw status
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Status: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere
Nginx Full                 ALLOW       Anywhere
OpenSSH (v6)               ALLOW       Anywhere (v6)
Nginx Full (v6)            ALLOW       Anywhere (v6)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="step-4-—-enabling-the-changes-in-nginx"&gt;Step 4 — Enabling the Changes in Nginx&lt;/h2&gt;

&lt;p&gt;Now that we've made our changes and adjusted our firewall, we can restart Nginx to implement our new changes.&lt;/p&gt;

&lt;p&gt;First, we should check to make sure that there are no syntax errors in our files. We can do this by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nginx -t
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If everything is successful, you will get a result that looks like this:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;nginx: [warn] "ssl_stapling" ignored, issuer certificate not found
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notice the warning in the beginning. As noted earlier, this particular setting throws a warning since our self-signed certificate can't use SSL stapling. This is expected and our server can still encrypt connections correctly.&lt;/p&gt;

&lt;p&gt;If your output matches the above, your configuration file has no syntax errors. We can safely restart Nginx to implement our changes:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl restart nginx
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="step-5-—-testing-encryption"&gt;Step 5 — Testing Encryption&lt;/h2&gt;

&lt;p&gt;Now, we're ready to test our SSL server.&lt;/p&gt;

&lt;p&gt;Open your web browser and type &lt;code&gt;https://&lt;/code&gt; followed by your server's domain name or IP into the address bar:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;https://&lt;span class="highlight"&gt;server_domain_or_IP&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Because the certificate we created isn't signed by one of your browser's trusted certificate authorities, you will likely see a scary looking warning like the one below (the following appears when using Google Chrome) :&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/nginx_ssl_1604/self_signed_warning.png" alt="Nginx self-signed cert warning"&gt;&lt;/p&gt;

&lt;p&gt;This is expected and normal. We are only interested in the encryption aspect of our certificate, not the third party validation of our host's authenticity. Click "ADVANCED" and then the link provided to proceed to your host anyways:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/nginx_ssl_1604/warning_override.png" alt="Nginx self-signed override"&gt;&lt;/p&gt;

&lt;p&gt;You should be taken to your site. If you look in the browser address bar, you will see a lock with an "x" over it. In this case, this just means that the certificate cannot be validated. It is still encrypting your connection.&lt;/p&gt;

&lt;p&gt;If you configured Nginx with two server blocks, automatically redirecting HTTP content to HTTPS, you can also check whether the redirect functions correctly:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;http://&lt;span class="highlight"&gt;server_domain_or_IP&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If this results in the same icon, this means that your redirect worked correctly.&lt;/p&gt;

&lt;h2 id="step-6-—-changing-to-a-permanent-redirect"&gt;Step 6 — Changing to a Permanent Redirect&lt;/h2&gt;

&lt;p&gt;If your redirect worked correctly and you are sure you want to allow only encrypted traffic, you should modify the Nginx configuration to make the redirect permanent.&lt;/p&gt;

&lt;p&gt;Open your server block configuration file again:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/nginx/sites-available/&lt;span class="highlight"&gt;example.com&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Find the &lt;code&gt;return 302&lt;/code&gt; and change it to &lt;code&gt;return 301&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-label " title="/etc/nginx/sites-available/example.com"&gt;/etc/nginx/sites-available/example.com&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;    return &lt;span class="highlight"&gt;301&lt;/span&gt; https://$server_name$request_uri;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save and close the file.&lt;/p&gt;

&lt;p&gt;Check your configuration for syntax errors:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nginx -t
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When you're ready, restart Nginx to make the redirect permanent:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl restart nginx
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;You have configured your Nginx server to use strong encryption for client connections. This will allow you serve requests securely, and will prevent outside parties from reading your traffic.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-install-and-configure-postfix-as-a-send-only-smtp-server-on-debian-9</id>
    <published>2018-09-07T18:19:23Z</published>
    <updated>2018-09-07T18:28:56Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-install-and-configure-postfix-as-a-send-only-smtp-server-on-debian-9"/>
    <title>How To Install and Configure Postfix as a Send-Only SMTP Server on Debian 9</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;Postfix is a &lt;em&gt;mail transfer agent&lt;/em&gt; (MTA), an application used to send and receive email. In this tutorial, you will install and configure Postfix so that it can be used to send emails by local applications only — that is, those installed on the same server as Postfix.&lt;/p&gt;

&lt;p&gt;Why would you want to do that?&lt;/p&gt;

&lt;p&gt;If you're already using a third-party email provider for sending and receiving emails, you do not need to run your own mail server. However, if you manage a cloud server on which you have installed applications that need to send email notifications, running a local, send-only SMTP server is a good alternative to using a third-party email service provider or running a full-blown SMTP server.&lt;/p&gt;

&lt;p&gt;In this tutorial, you'll install and configure Postfix as a send-only SMTP server on Debian 9.&lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;To follow this tutorial, you will need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;One Debian 9 server, set up with the &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-debian-9"&gt;Debian 9 initial server setup tutorial&lt;/a&gt;, and a sudo non-root user.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A valid domain name, like &lt;strong&gt;example.com&lt;/strong&gt;, pointing to your server. You can set that up by following these &lt;a href="https://www.digitalocean.com/docs/networking/dns/"&gt;guidelines on managing DNS hosting on DigitalOcean&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note that your server's hostname should match your domain or subdomain. You can verify the server's hostname by typing &lt;code&gt;hostname&lt;/code&gt; at the command prompt. The output should match the name you gave the server when it was being created.&lt;/p&gt;

&lt;h2 id="step-1-—-installing-postfix"&gt;Step 1 — Installing Postfix&lt;/h2&gt;

&lt;p&gt;In this step, you'll learn how to install Postfix. You will need two packages: &lt;code&gt;mailutils&lt;/code&gt;, which includes programs necessary for Postfix to function, and &lt;code&gt;postfix&lt;/code&gt; itself.  &lt;/p&gt;

&lt;p&gt;First, update the package database:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt update
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, install &lt;code&gt;mailtuils&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install mailutils
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, install &lt;code&gt;postfix&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install postfix
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Near the end of the installation process, you will be presented with a window that looks like the one in the image below. The default option is &lt;strong&gt;Internet Site&lt;/strong&gt;. That's the recommended option for this tutorial, so press &lt;code&gt;TAB&lt;/code&gt;, then &lt;code&gt;ENTER&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/postfix-16.04/zJuFrgI.png?1" alt="Select Internet Site from the menu, then press TAB to select &amp;lt;Ok&amp;gt;, then ENTER"&gt;&lt;/p&gt;

&lt;p&gt;After that, you'll get another window just like the one in the next image. The &lt;strong&gt;System mail name&lt;/strong&gt; should be the same as the name you assigned to the server when you were creating it. If it shows a subdomain like &lt;code&gt;subdomain.example.com&lt;/code&gt;, change it to just &lt;code&gt;example.com&lt;/code&gt;. When you've finished, press &lt;code&gt;TAB&lt;/code&gt;, then &lt;code&gt;ENTER&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/postfix-16.04/sVEi9SW.png?1" alt="Enter your domain name, then press TAB to select &amp;lt;Ok&amp;gt;, ENTER"&gt;&lt;/p&gt;

&lt;p&gt;You now have Postfix installed and are ready to modify its configuration settings. &lt;/p&gt;

&lt;h2 id="step-2-—-configuring-postfix"&gt;Step 2 — Configuring Postfix&lt;/h2&gt;

&lt;p&gt;In this step, you'll configure Postfix to process requests to send emails only from the server on which it is running, i.e. from &lt;code&gt;localhost&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For that to happen, Postfix needs to be configured to listen only on the &lt;em&gt;loopback interface&lt;/em&gt;, the virtual network interface that the server uses to communicate internally. To make the change, open the main Postfix configuration file using &lt;code&gt;nano&lt;/code&gt; or &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-debian-9#step-six-%E2%80%94-completing-optional-configuration"&gt;your favorite text editor&lt;/a&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/postfix/main.cf
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With the file open, scroll down until you see the following section:&lt;/p&gt;
&lt;div class="code-label " title="/etc/postfix/main.cf"&gt;/etc/postfix/main.cf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .
mailbox_size_limit = 0
recipient_delimiter = +
inet_interfaces = all
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Change the line that reads &lt;code&gt;inet_interfaces = all&lt;/code&gt; to &lt;code&gt;inet_interfaces = loopback-only&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-label " title="/etc/postfix/main.cf"&gt;/etc/postfix/main.cf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .
mailbox_size_limit = 0
recipient_delimiter = +
inet_interfaces = &lt;span class="highlight"&gt;loopback-only&lt;/span&gt;
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Another directive you'll need to modify is &lt;code&gt;mydestination&lt;/code&gt;, which is used to specify the list of domains that are delivered via the &lt;code&gt;local_transport&lt;/code&gt; mail delivery transport. By default, the values are similar to these:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="/etc/postfix/main.cf"&gt;/etc/postfix/main.cf&lt;/div&gt;. . .
mydestination = $myhostname, &lt;span class="highlight"&gt;example.com&lt;/span&gt;, localhost.com, , localhost
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;a href="http://www.postfix.org/postconf.5.html#mydestination"&gt;recommended defaults&lt;/a&gt; for this directive are given in the code block below, so modify yours to match:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="/etc/postfix/main.cf"&gt;/etc/postfix/main.cf&lt;/div&gt;. . .
mydestination = $myhostname, &lt;span class="highlight"&gt;localhost.$your_domain, $your_domain&lt;/span&gt;
. . .

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save and close the file.&lt;/p&gt;

&lt;p&gt;&lt;span class='note'&gt;&lt;strong&gt;Note:&lt;/strong&gt; If you're hosting multiple domains on a single server, the other domains can also be passed to Postfix using the &lt;code&gt;mydestination&lt;/code&gt; directive. However, to configure Postfix in a manner that scales and that does not present issues for such a setup involves additional configurations that are beyond the scope of this article.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;Finally, restart Postfix.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl restart postfix
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="step-3-—-testing-the-smtp-server"&gt;Step 3 — Testing the SMTP Server&lt;/h2&gt;

&lt;p&gt;In this step, you'll test whether Postfix can send emails to an external email account using the &lt;code&gt;mail&lt;/code&gt; command, which is part of the &lt;code&gt;mailutils&lt;/code&gt; package you installed in Step 1.&lt;/p&gt;

&lt;p&gt;To send a test email, type:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;echo "This is the body of the email" | mail -s "This is the subject line" &lt;span class="highlight"&gt;your_email_address&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In performing your own test(s), you may use the body and subject line text as-is, or change them to your liking. However, in place of &lt;code&gt;&lt;span class="highlight"&gt;your_email_address&lt;/span&gt;&lt;/code&gt;, use a valid email address. The domain part can be &lt;code&gt;gmail.com&lt;/code&gt;, &lt;code&gt;fastmail.com&lt;/code&gt;, &lt;code&gt;yahoo.com&lt;/code&gt;, or any other email service provider that you use.&lt;/p&gt;

&lt;p&gt;Now check the email address where you sent the test message. You should see the message in your Inbox. If not, check your Spam folder.&lt;/p&gt;

&lt;p&gt;Note that with this configuration, the address in the &lt;strong&gt;From&lt;/strong&gt; field for the test emails you send will be &lt;code&gt;&lt;span class="highlight"&gt;sammy&lt;/span&gt;@&lt;span class="highlight"&gt;example.com&lt;/span&gt;&lt;/code&gt;, where &lt;strong&gt;sammy&lt;/strong&gt; is your Linux username and the domain is the server's hostname. If you change your username, the &lt;strong&gt;From&lt;/strong&gt; address will also change.&lt;/p&gt;

&lt;h2 id="step-4-—-forwarding-system-mail"&gt;Step 4 — Forwarding System Mail&lt;/h2&gt;

&lt;p&gt;The last thing we want to set up is forwarding, so you'll get emails sent to &lt;strong&gt;root&lt;/strong&gt; on the system at your personal, external email address.&lt;/p&gt;

&lt;p&gt;To configure Postfix so that system-generated emails will be sent to your email address, you need to edit the &lt;code&gt;/etc/aliases&lt;/code&gt; file:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/aliases
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The full contents of the file on a default installation of Debian 9 are as follows:&lt;/p&gt;
&lt;div class="code-label " title="/etc/aliases"&gt;/etc/aliases&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;mailer-daemon: postmaster
postmaster: root
nobody: root
hostmaster: root
usenet: root
news: root
webmaster: root
www: root
ftp: root
abuse: root
noc: root
security: root
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;postmaster: root&lt;/code&gt; setting ensures that system-generated emails are sent to the root user. You want to edit these settings so these emails are rerouted to your email address. To accomplish that, edit the file so that it reads:&lt;/p&gt;
&lt;div class="code-label " title="/etc/aliases"&gt;/etc/aliases&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;mailer-daemon: postmaster
postmaster:    root
&lt;span class="highlight"&gt;root:          your_email_address&lt;/span&gt;
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Replace &lt;code&gt;&lt;span class="highlight"&gt;your_email_address&lt;/span&gt;&lt;/code&gt; with your personal email address. When finished, save and close the file. For the change to take effect, run the following command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo newaliases
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can test that it works by sending an email to the root account using:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;echo "This is the body of the email" | mail -s "This is the subject line" root
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should receive the email at your email address. If not, check your Spam folder.&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;That's all it takes to set up a send-only email server using Postfix. You may want to take some additional steps to protect your domain from spammers, however.&lt;/p&gt;

&lt;p&gt;If you want to receive notifications from your server at a single address, then having emails marked as Spam is less of an issue because you can create a whitelist workaround. However, if you want to send emails to potential site users (such as confirmation emails for a message board sign-up), you should definitely set up SPF records and DKIM so your server's emails are more likely to be seen as legitimate.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.digitalocean.com/community/tutorials/how-to-use-an-spf-record-to-prevent-spoofing-improve-e-mail-reliability"&gt;How To Use an SPF Record to Prevent Spoofing &amp;amp; Improve E-mail Reliability&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-and-configure-dkim-with-postfix-on-debian-wheezy"&gt;How To Install and Configure DKIM with Postfix on Debian Wheezy&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If configured correctly, these steps make it difficult to send Spam with an address that appears to originate from your domain. Taking these additional configuration steps will also make it more likely for common mail providers to see emails from your server as legitimate.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-install-hadoop-in-stand-alone-mode-on-debian-9</id>
    <published>2018-09-07T16:57:50Z</published>
    <updated>2018-09-07T17:00:58Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-install-hadoop-in-stand-alone-mode-on-debian-9"/>
    <title>How to Install Hadoop in Stand-Alone Mode on Debian 9</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;Hadoop is a Java-based programming framework that supports the processing and storage of extremely large datasets on a cluster of inexpensive machines. It was the first major open source project in the big data playing field and is sponsored by the Apache Software Foundation.&lt;/p&gt;

&lt;p&gt;Hadoop is comprised of four main layers: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Hadoop Common&lt;/strong&gt; is the collection of utilities and libraries that support other Hadoop modules.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HDFS&lt;/strong&gt;, which stands for Hadoop Distributed File System, is responsible for persisting data to disk.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;YARN&lt;/strong&gt;, short for Yet Another Resource Negotiator, is the "operating system" for HDFS.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MapReduce&lt;/strong&gt; is the original processing model for Hadoop clusters. It distributes work within the cluster or map, then organizes and reduces the results from the nodes into a response to a query. Many other processing models are available for the 3.x version of Hadoop.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Hadoop clusters are relatively complex to set up, so the project includes a stand-alone mode which is suitable for learning about Hadoop, performing simple operations, and debugging.&lt;/p&gt;

&lt;p&gt;In this tutorial, you'll install Hadoop in stand-alone mode and run one of the example example MapReduce programs it includes to verify the installation.&lt;/p&gt;

&lt;p&gt;Before you begin, you might also like to take a look at &lt;a href="https://www.digitalocean.com/community/tutorials/an-introduction-to-big-data-concepts-and-terminology"&gt;An Introduction to Big Data Concepts and Terminology&lt;/a&gt; or &lt;a href="https://www.digitalocean.com/community/tutorials/an-introduction-to-hadoop"&gt;An Introduction to Hadoop&lt;/a&gt;&lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;To follow this tutorial, you will need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;A Debian 9 server with a non-root user with &lt;code&gt;sudo&lt;/code&gt; privileges&lt;/strong&gt; and a firewall, which you can set up by following the &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-debian-9"&gt;Initial Server Setup with Debian 9&lt;/a&gt; tutorial.&lt;/li&gt;
&lt;li&gt;Java installed by following &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-java-with-apt-on-debian-9"&gt;How to Install Java with Apt on Debian 9&lt;/a&gt;. You can use OpenJDK for this tutorial.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;JAVA_HOME&lt;/code&gt; environment variable set in &lt;code&gt;/etc/environment&lt;/code&gt;, as shown in &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-java-with-apt-on-debian-9#setting-the-java_home-environment-variable"&gt;How to Install Java with Apt on Debian 9&lt;/a&gt;. Hadoop requires this variable to be set.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="step-1-—-installing-hadoop"&gt;Step 1 — Installing Hadoop&lt;/h2&gt;

&lt;p&gt;To install Hadoop, first visit the &lt;a href="http://hadoop.apache.org/releases.html"&gt;Apache Hadoop Releases page&lt;/a&gt; to find the most recent stable release.&lt;/p&gt;

&lt;p&gt;Navigate to &lt;strong&gt;binary&lt;/strong&gt; for the release you’d like to install. In this guide, we’ll install Hadoop 3.0.3.&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/hadoop-standalone-1804/hadoop-download-link.png" alt="Screenshot of the Hadoop releases page highlighting the link to the latest stable binary"&gt;&lt;/p&gt;

&lt;p&gt;On the next page, right-click and copy the link to the release binary.&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/hadoop-standalone-1804/hadoop-binary-link.png" alt="Screenshot of the Hadoop mirror page"&gt;&lt;/p&gt;

&lt;p&gt;On your server, use &lt;code&gt;wget&lt;/code&gt; to fetch it:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;wget http://&lt;span class="highlight"&gt;www-us.apache.org&lt;/span&gt;/dist/hadoop/common/hadoop-&lt;span class="highlight"&gt;3.0.3&lt;/span&gt;/hadoop-&lt;span class="highlight"&gt;3.0.3&lt;/span&gt;.tar.gz
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span class='note'&gt;&lt;strong&gt;Note:&lt;/strong&gt; The Apache website will direct you to the best mirror dynamically, so your URL may not match  the URL above.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;In order to ensure that the file you downloaded hasn't been altered, do a quick check using SHA-256. Return to the &lt;a href="http://hadoop.apache.org/releases.html"&gt;releases page&lt;/a&gt;, then right-click and copy the link to the checksum file for the release binary you downloaded:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/hadoop-standalone-1804/hadoop-checksum-link.png" alt="Screenshot highlighting the .mds file"&gt;&lt;/p&gt;

&lt;p&gt;Again, use &lt;code&gt;wget&lt;/code&gt; on your server to download the file:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;wget https://dist.apache.org/repos/dist/release/hadoop/common/hadoop-&lt;span class="highlight"&gt;3.0.3&lt;/span&gt;/hadoop-&lt;span class="highlight"&gt;3.0.3&lt;/span&gt;.tar.gz.mds
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then run the verification:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sha256sum hadoop-&lt;span class="highlight"&gt;3.0.3&lt;/span&gt;.tar.gz
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;db96e2c0d0d5352d8984892dfac4e27c0e682d98a497b7e04ee97c3e2019277a  hadoop-&lt;span class="highlight"&gt;3.0.3&lt;/span&gt;.tar.gz
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Compare this value with the SHA-256 value in the &lt;code&gt;.mds&lt;/code&gt; file:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cat hadoop-&lt;span class="highlight"&gt;3.0.3&lt;/span&gt;.tar.gz.mds | grep SHA256
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class="code-label " title="~/hadoop-3.0.3.tar.gz.mds"&gt;~/hadoop-3.0.3.tar.gz.mds&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;...
SHA256 = DB96E2C0 D0D5352D 8984892D FAC4E27C 0E682D98 A497B7E0 4EE97C3E 2019277A
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can safely ignore the difference in case and the spaces. The output of the command you ran against the file we downloaded from the mirror should match the value in the file you downloaded from apache.org.&lt;/p&gt;

&lt;p&gt;Now that you've verified that the file wasn't corrupted or changed, use the &lt;code&gt;tar&lt;/code&gt; command with the &lt;code&gt;-x&lt;/code&gt; flag to extract, &lt;code&gt;-z&lt;/code&gt; to uncompress, &lt;code&gt;-v&lt;/code&gt; for verbose output, and &lt;code&gt;-f&lt;/code&gt; to specify that you're extracting the archive from a file. Use tab-completion or substitute the correct version number in the command below:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;tar -xzvf hadoop-&lt;span class="highlight"&gt;3.0.3&lt;/span&gt;.tar.gz
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, move the extracted files into &lt;code&gt;/usr/local&lt;/code&gt;, the appropriate place for locally installed software. Change the version number, if needed, to match the version you downloaded.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo mv hadoop-&lt;span class="highlight"&gt;3.0.3&lt;/span&gt; /usr/local/hadoop
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With the software in place, we're ready to configure its environment.&lt;/p&gt;

&lt;h2 id="step-3-—-running-hadoop"&gt;Step 3 — Running Hadoop&lt;/h2&gt;

&lt;p&gt;Let's make sure Hadoop runs. Execute the following command to launch Hadoop and display its help options:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;/usr/local/hadoop/bin/hadoop
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You'll see the following output, which lets you know you've successfully configured Hadoop to run in stand-alone mode. &lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Usage: hadoop [OPTIONS] SUBCOMMAND [SUBCOMMAND OPTIONS]
 or    hadoop [OPTIONS] CLASSNAME [CLASSNAME OPTIONS]
  where CLASSNAME is a user-provided Java class

  OPTIONS is none or any of:

--config dir                     Hadoop config directory
--debug                          turn on shell script debug mode
--help                           usage information
buildpaths                       attempt to add class files from build tree
hostnames list[,of,host,names]   hosts to use in slave mode
hosts filename                   list of hosts to use in slave mode
loglevel level                   set the log4j level for this command
workers                          turn on worker mode

  SUBCOMMAND is one of:
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We'll ensure that it is functioning properly by running the example MapReduce program it ships with. To do so, create a directory called &lt;code&gt;input&lt;/code&gt; in your home directory and copy Hadoop's configuration files into it to use those files as our data. &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;mkdir ~/input
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;cp /usr/local/hadoop/etc/hadoop/*.xml ~/input
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, we'll run the MapReduce &lt;code&gt;hadoop-mapreduce-examples&lt;/code&gt; program, a Java archive with several options. We'll invoke its &lt;code&gt;grep&lt;/code&gt; program, one of the many examples included in &lt;code&gt;hadoop-mapreduce-examples&lt;/code&gt;, followed by the input directory, &lt;code&gt;input&lt;/code&gt; and the output directory &lt;code&gt;grep_example&lt;/code&gt;. The MapReduce grep program will count the matches of a literal word or regular expression. Finally, we'll supply the regular expression &lt;code&gt;allowed[.]*&lt;/code&gt; to find occurrences of the word &lt;code&gt;allowed&lt;/code&gt; within or at the end of a declarative sentence. The expression is case-sensitive, so we wouldn't find the word if it were capitalized at the beginning of a sentence.&lt;/p&gt;

&lt;p&gt;Execute the following command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;/usr/local/hadoop/bin/hadoop jar /usr/local/hadoop/share/hadoop/mapreduce/hadoop-mapreduce-examples-&lt;span class="highlight"&gt;3.0.3&lt;/span&gt;.jar grep ~/input ~/grep_example 'allowed[.]*'
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When the task completes, it provides a summary of what has been processed and errors it has encountered, but this doesn't contain the actual results:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt; . . .
        File System Counters
        FILE: Number of bytes read=1330690
        FILE: Number of bytes written=3128841
        FILE: Number of read operations=0
        FILE: Number of large read operations=0
        FILE: Number of write operations=0
    Map-Reduce Framework
        Map input records=2
        Map output records=2
        Map output bytes=33
        Map output materialized bytes=43
        Input split bytes=115
        Combine input records=0
        Combine output records=0
        Reduce input groups=2
        Reduce shuffle bytes=43
        Reduce input records=2
        Reduce output records=2
        Spilled Records=4
        Shuffled Maps =1
        Failed Shuffles=0
        Merged Map outputs=1
        GC time elapsed (ms)=3
        Total committed heap usage (bytes)=478150656
    Shuffle Errors
        BAD_ID=0
        CONNECTION=0
        IO_ERROR=0
        WRONG_LENGTH=0
        WRONG_MAP=0
        WRONG_REDUCE=0
    File Input Format Counters
        Bytes Read=147
    File Output Format Counters
        Bytes Written=34
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The results are stored in the &lt;code&gt;~/grep_example&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;If this output directory already exists, the program will fail, and rather than seeing the summary, you'll see something like this:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt; . . .
    at java.base/java.lang.reflect.Method.invoke(Method.java:564)
    at org.apache.hadoop.util.RunJar.run(RunJar.java:244)
    at org.apache.hadoop.util.RunJar.main(RunJar.java:158)

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Check the results by running &lt;code&gt;cat&lt;/code&gt; on the output directory:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cat ~/grep_example/*
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You'll see this output:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;19  allowed.
1   allowed
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The MapReduce task found 19 occurrences of the word &lt;code&gt;allowed&lt;/code&gt; followed by a period and one occurrence where it was not. Running the example program has verified that our stand-alone installation is working properly and that non-privileged users on the system can run Hadoop for exploration or debugging.&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;In this tutorial, we've installed Hadoop in stand-alone mode and verified it by running an example program it provided. To learn how to write your own MapReduce programs, visit Apache Hadoop's &lt;a href="https://hadoop.apache.org/docs/stable/hadoop-mapreduce-client/hadoop-mapreduce-client-core/MapReduceTutorial.html"&gt;MapReduce tutorial&lt;/a&gt; which walks through the code behind the example you used in this tutorial. When you're ready to set up a cluster, see the Apache Foundation &lt;a href="https://hadoop.apache.org/docs/stable/hadoop-project-dist/hadoop-common/ClusterSetup.html"&gt;Hadoop Cluster Setup&lt;/a&gt; guide.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-rewrite-urls-with-mod-rewrite-for-apache-on-debian-9</id>
    <published>2018-09-07T16:40:21Z</published>
    <updated>2018-09-07T16:40:42Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-rewrite-urls-with-mod-rewrite-for-apache-on-debian-9"/>
    <title>How To Rewrite URLs with mod_rewrite for Apache on Debian 9</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;Apache's &lt;code&gt;mod_rewrite&lt;/code&gt; module lets you rewrite URLs in a cleaner fashion, translating human-readable paths into code-friendly query strings. It also lets you rewrite URLs based on conditions.&lt;/p&gt;

&lt;p&gt;An &lt;code&gt;.htaccess&lt;/code&gt; file lets you create and apply rewrite rules without accessing server configuration files. By placing the &lt;code&gt;.htaccess&lt;/code&gt; file in the root of your web site, you can manage rewrites on a per-site or per-directory basis.&lt;/p&gt;

&lt;p&gt;In this tutorial, you'll enable &lt;code&gt;mod_rewrite&lt;/code&gt; and use &lt;code&gt;.htaccess&lt;/code&gt; files to create a basic URL redirection, and then explore a couple of advanced use cases.&lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;To follow this tutorial, you will need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;One Debian 9 server set up by following the &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-debian-9"&gt;Debian 9 initial server setup guide&lt;/a&gt;, including a sudo non-root user and a firewall.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Apache installed by following Steps 1 and 2 of  &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-the-apache-web-server-on-debian-9"&gt;How To Install the Apache Web Server on Debian 9&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="step-1-—-enabling-mod_rewrite"&gt;Step 1 — Enabling mod_rewrite&lt;/h2&gt;

&lt;p&gt;In order for Apache to understand rewrite rules, we first need to activate &lt;code&gt;mod_rewrite&lt;/code&gt;. It's already installed, but it's disabled on a default Apache installation. Use the &lt;code&gt;a2enmod&lt;/code&gt; command to enable the module:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo a2enmod rewrite
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will activate the module or alert you that the module is already enabled. To put these changes into effect, restart Apache:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl restart apache2
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;mod_rewrite&lt;/code&gt; is now fully enabled. In the next step we will set up an &lt;code&gt;.htaccess&lt;/code&gt; file that we'll use to define rewrite rules for redirects.&lt;/p&gt;

&lt;h2 id="step-2-—-setting-up-htaccess"&gt;Step 2 — Setting Up .htaccess&lt;/h2&gt;

&lt;p&gt;An &lt;code&gt;.htaccess&lt;/code&gt; file allows us to modify our rewrite rules without accessing server configuration files. For this reason, &lt;code&gt;.htaccess&lt;/code&gt; is critical to your web application's security. The period that precedes the filename ensures that the file is hidden.&lt;/p&gt;

&lt;span class='note'&gt;&lt;p&gt;
&lt;strong&gt;Note:&lt;/strong&gt; Any rules that you can put in an &lt;code&gt;.htaccess&lt;/code&gt; file can also be put directly into server configuration files. In fact, the &lt;a href="http://httpd.apache.org/docs/2.4/howto/htaccess.html"&gt;official Apache documentation&lt;/a&gt; recommends using server configuration files instead of &lt;code&gt;.htaccess&lt;/code&gt; thanks to faster processing times.&lt;/p&gt;

&lt;p&gt;However, in this simple example, the performance increase will be negligible. Additionally, setting rules in &lt;code&gt;.htaccess&lt;/code&gt; is convenient, especially with multiple websites on the same server. It does not require a server restart for changes to take effect or root privileges to edit rules, simplifying maintenance and the process of making changes with an unprivileged account. Popular open-source software like Wordpress and Joomla relies on &lt;code&gt;.htaccess&lt;/code&gt; files to make modifications and additional rules on demand.&lt;br&gt;&lt;/p&gt;&lt;/span&gt;

&lt;p&gt;Before you start using &lt;code&gt;.htaccess&lt;/code&gt; files, you'll need to set up and secure a few more settings.&lt;/p&gt;

&lt;p&gt;By default, Apache prohibits using an &lt;code&gt;.htaccess&lt;/code&gt; file to apply rewrite rules, so first you need to allow changes to the file. Open the default Apache configuration file using &lt;code&gt;nano&lt;/code&gt; or &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-debian-9#step-six-%E2%80%94-completing-optional-configuration"&gt;your favorite text editor&lt;/a&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/apache2/sites-available/000-default.conf
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Inside that file, you will find a &lt;code&gt;&amp;lt;VirtualHost *:80&amp;gt;&lt;/code&gt; block starting on the first line. Inside of that block, add the following new block so your configuration file looks like the following. Make sure that all blocks are properly indented.&lt;/p&gt;
&lt;div class="code-label " title="/etc/apache2/sites-available/000-default.conf"&gt;/etc/apache2/sites-available/000-default.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&amp;lt;VirtualHost *:80&amp;gt;
    &lt;span class="highlight"&gt;&amp;lt;Directory /var/www/html&amp;gt;&lt;/span&gt;
        &lt;span class="highlight"&gt;Options Indexes FollowSymLinks&lt;/span&gt;
        &lt;span class="highlight"&gt;AllowOverride All&lt;/span&gt;
        &lt;span class="highlight"&gt;Require all granted&lt;/span&gt;
    &lt;span class="highlight"&gt;&amp;lt;/Directory&amp;gt;&lt;/span&gt;

    . . .
&amp;lt;/VirtualHost&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save and close the file. &lt;/p&gt;

&lt;p&gt;Check your configuration:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apache2ctl configtest
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If there are no errors, restart Apache to put your changes into effect:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl restart apache2
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, create an &lt;code&gt;.htaccess&lt;/code&gt; file in the web root:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /var/www/html/.htaccess
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add this line at the top of the new file to activate the rewrite engine.&lt;/p&gt;
&lt;div class="code-label " title="/var/www/html/.htaccess"&gt;/var/www/html/.htaccess&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;RewriteEngine on
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save the file and exit.&lt;/p&gt;

&lt;p&gt;You now have an operational &lt;code&gt;.htaccess&lt;/code&gt; file that you can use to govern your web application's routing rules. In the next step, we will create sample website files that we'll use to demonstrate rewrite rules.&lt;/p&gt;

&lt;h2 id="step-3-—-configuring-url-rewrites"&gt;Step 3 — Configuring URL Rewrites&lt;/h2&gt;

&lt;p&gt;Here, we will set up a basic URL rewrite which converts pretty URLs into actual paths to pages. Specifically, we will allow users to access &lt;code&gt;http://&lt;span class="highlight"&gt;your_server_ip&lt;/span&gt;/about&lt;/code&gt;, and display a page called &lt;code&gt;about.html&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Begin by creating a file named &lt;code&gt;about.html&lt;/code&gt; in the web root:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /var/www/html/about.html
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Copy the following HTML code into the file, then save and close it.&lt;/p&gt;
&lt;div class="code-label " title="/var/www/html/about.html"&gt;/var/www/html/about.html&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-html"&gt;&amp;lt;html&amp;gt;
    &amp;lt;head&amp;gt;
        &amp;lt;title&amp;gt;About Us&amp;lt;/title&amp;gt;
    &amp;lt;/head&amp;gt;
    &amp;lt;body&amp;gt;
        &amp;lt;h1&amp;gt;About Us&amp;lt;/h1&amp;gt;
    &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can access this page at &lt;code&gt;http://&lt;span class="highlight"&gt;your_server_ip&lt;/span&gt;/about.html&lt;/code&gt;, but notice that if you try to access &lt;code&gt;http://&lt;span class="highlight"&gt;your_server_ip&lt;/span&gt;/about&lt;/code&gt;, you will see a &lt;strong&gt;404 Not Found&lt;/strong&gt; error. To access the page using &lt;code&gt;/about&lt;/code&gt; instead, we'll create a rewrite rule.&lt;/p&gt;

&lt;p&gt;All &lt;code&gt;RewriteRules&lt;/code&gt; follow this format:&lt;/p&gt;
&lt;div class="code-label " title="General RewriteRule structure"&gt;General RewriteRule structure&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;RewriteRule pattern substitution [flags]
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;RewriteRule&lt;/code&gt; specifies the directive.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pattern&lt;/code&gt; is a &lt;a href="https://www.digitalocean.com/community/tutorials/an-introduction-to-regular-expressions"&gt;regular expression&lt;/a&gt; that matches the desired string from the URL, which is what the viewer types in the browser.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;substitution&lt;/code&gt; is the path to the actual URL, i.e. the path of the file Apache serves.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;flags&lt;/code&gt; are optional parameters that can modify how the rule works.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's create our URL rewrite rule. Open up the &lt;code&gt;.htaccess&lt;/code&gt; file:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /var/www/html/.htaccess
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After the first line, add the following &lt;code&gt;RewriteRule&lt;/code&gt; and save the file:&lt;/p&gt;
&lt;div class="code-label " title="/var/www/html/.htaccess"&gt;/var/www/html/.htaccess&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;RewriteEngine on
&lt;span class="highlight"&gt;RewriteRule ^about$ about.html [NC]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this case, &lt;code&gt;^about$&lt;/code&gt; is the pattern, &lt;code&gt;about.html&lt;/code&gt; is the substitution, and &lt;code&gt;[NC]&lt;/code&gt; is a flag. Our example uses a few characters with special meaning:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;^&lt;/code&gt; indicates the start of the URL, after &lt;code&gt;&lt;span class="highlight"&gt;your_server_ip&lt;/span&gt;/&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$&lt;/code&gt; indicates the end of the URL.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;about&lt;/code&gt; matches the string "about".&lt;/li&gt;
&lt;li&gt;&lt;code&gt;about.html&lt;/code&gt; is the actual file that the user accesses.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[NC]&lt;/code&gt; is a flag that makes the rule case insensitive.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can now access &lt;code&gt;http://&lt;span class="highlight"&gt;your_server_ip&lt;/span&gt;/about&lt;/code&gt; in your browser. In fact, with the rule shown above, the following URLs will also point to &lt;code&gt;about.html&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;http://&lt;span class="highlight"&gt;your_server_ip&lt;/span&gt;/about&lt;/code&gt;, because of the rule definition.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;http://&lt;span class="highlight"&gt;your_server_ip&lt;/span&gt;/About&lt;/code&gt;, because the rule is case insensitive.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;http://&lt;span class="highlight"&gt;your_server_ip&lt;/span&gt;/about.html&lt;/code&gt;, because the original filename will always work.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, the following will not work:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;http://&lt;span class="highlight"&gt;your_server_ip&lt;/span&gt;/about/&lt;/code&gt;, because the rule explicitly states that there may be nothing after &lt;code&gt;about&lt;/code&gt;, since the &lt;code&gt;$&lt;/code&gt; character appears after &lt;code&gt;about&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;http://&lt;span class="highlight"&gt;your_server_ip&lt;/span&gt;/contact&lt;/code&gt;, because it won't match the &lt;code&gt;about&lt;/code&gt; string in the rule.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You now have an operational &lt;code&gt;.htaccess&lt;/code&gt; file with a basic rule that you can modify and extend to your needs. In the following sections, we will show two additional examples of commonly used directives.&lt;/p&gt;

&lt;h2 id="example-1-—-simplifying-query-strings-with-rewriterule"&gt;Example 1 — Simplifying Query Strings with RewriteRule&lt;/h2&gt;

&lt;p&gt;Web applications often make use of &lt;em&gt;query strings&lt;/em&gt;, which are appended to a URL using a question mark (&lt;code&gt;?&lt;/code&gt;) after the address. Separate parameters are delimited using an ampersand (&lt;code&gt;&amp;amp;&lt;/code&gt;). Query strings may be used for passing additional data between individual application pages.&lt;/p&gt;

&lt;p&gt;For example, a search result page written in PHP may use a URL like &lt;code&gt;http://example.com/results.php?item=shirt&amp;amp;season=summer&lt;/code&gt;. In this example, two additional parameters are passed to the imaginary &lt;code&gt;result.php&lt;/code&gt; application script: &lt;code&gt;item&lt;/code&gt;, with the value &lt;code&gt;shirt&lt;/code&gt;, and &lt;code&gt;season&lt;/code&gt; with the value &lt;code&gt;summer&lt;/code&gt;. The application may use the query string information to build the right page for the visitor.&lt;/p&gt;

&lt;p&gt;Apache rewrite rules are often employed to simplify such long and unpleasant links as the example above into &lt;em&gt;friendly URLs&lt;/em&gt; that are easier to type and interpret visually. In this example, we would like to simplify the above link to become &lt;code&gt;http://example.com/shirt/summer&lt;/code&gt;. The &lt;code&gt;shirt&lt;/code&gt; and &lt;code&gt;summer&lt;/code&gt; parameter values are still in the address, but without the query string and script name.&lt;/p&gt;

&lt;p&gt;Here's one rule to implement this:&lt;/p&gt;
&lt;div class="code-label " title="Simple substition"&gt;Simple substition&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;RewriteRule ^&lt;span class="highlight"&gt;shirt&lt;/span&gt;/&lt;span class="highlight"&gt;summer&lt;/span&gt;$ results.php?item=&lt;span class="highlight"&gt;shirt&lt;/span&gt;&amp;amp;season=&lt;span class="highlight"&gt;summer&lt;/span&gt; [QSA]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;shirt/summer&lt;/code&gt; is explicitly matched in the requested address and Apache is told to serve &lt;code&gt;results.php?item=shirt&amp;amp;season=summer&lt;/code&gt; instead. &lt;/p&gt;

&lt;p&gt;The &lt;code&gt;[QSA]&lt;/code&gt; flags are commonly used in rewrite rules. They tell Apache to append any additional query string to the served URL, so if the visitor types &lt;code&gt;http://example.com/shirt/summer?&lt;span class="highlight"&gt;page=2&lt;/span&gt;&lt;/code&gt; the server will respond with &lt;code&gt;results.php?item=shirt&amp;amp;season=summer&lt;span class="highlight"&gt;&amp;amp;page=2&lt;/span&gt;&lt;/code&gt;. Without it, the additional query string would get discarded.&lt;/p&gt;

&lt;p&gt;While this method achieves the desired effect, both the item name and season are hardcoded into the rule. This means the rule will not work for any other items, like &lt;code&gt;pants&lt;/code&gt;, or seasons, like &lt;code&gt;winter&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To make the rule more generic, we can use &lt;a href="https://www.digitalocean.com/community/tutorials/an-introduction-to-regular-expressions"&gt;regular expressions&lt;/a&gt; to match parts of the original address and use those parts in a substitution pattern. The modified rule will then look like this:&lt;/p&gt;
&lt;div class="code-label " title="Simple substition"&gt;Simple substition&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;RewriteRule ^&lt;span class="highlight"&gt;([A-Za-z0-9]+)&lt;/span&gt;/&lt;span class="highlight"&gt;(summer|winter|fall|spring)&lt;/span&gt; results.php?item=$1&amp;amp;season=$2 [QSA]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first regular expression group in parenthesis matches a string containing alphanumeric characters and numbers like &lt;code&gt;shirt&lt;/code&gt; or &lt;code&gt;pants&lt;/code&gt; and saves the matched fragment as the &lt;code&gt;$1&lt;/code&gt; variable. The second regular expression group in parentheses matches exactly &lt;code&gt;summer&lt;/code&gt;, &lt;code&gt;winter&lt;/code&gt;, &lt;code&gt;fall&lt;/code&gt;, or &lt;code&gt;spring&lt;/code&gt;, and similarly saves the matched fragment as &lt;code&gt;$2&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The matched fragments are then used in the resulting URL in &lt;code&gt;item&lt;/code&gt; and &lt;code&gt;season&lt;/code&gt; variables instead of the hardcoded &lt;code&gt;shirt&lt;/code&gt; and &lt;code&gt;summer&lt;/code&gt; values we used before.&lt;/p&gt;

&lt;p&gt;The above will convert, for example, &lt;code&gt;http://example.com/pants/summer&lt;/code&gt; into &lt;code&gt;http://example.com/results.php?item=pants&amp;amp;season=summer&lt;/code&gt;. This example is also future proof, allowing multiple items and seasons to be correctly rewritten using a single rule.&lt;/p&gt;

&lt;h2 id="example-2-—-adding-conditions-with-logic-using-rewriteconds"&gt;Example 2 — Adding Conditions with Logic Using RewriteConds&lt;/h2&gt;

&lt;p&gt;Rewrite rules are not necessarily always evaluated one by one without any limitations. The &lt;code&gt;RewriteCond&lt;/code&gt; directive lets us add conditions to our rewrite rules to control when the rules will be processed. All &lt;code&gt;RewriteConds&lt;/code&gt; abide by the following format:&lt;/p&gt;
&lt;div class="code-label " title="General RewriteCond structure"&gt;General RewriteCond structure&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;RewriteCond TestString Condition [Flags]
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;RewriteCond&lt;/code&gt; specifies the &lt;code&gt;RewriteCond&lt;/code&gt; directive.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TestString&lt;/code&gt; is the string to test against.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Condition&lt;/code&gt; is the pattern or condition to match.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Flags&lt;/code&gt; are optional parameters that may modify the condition and evaluation rules.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If a &lt;code&gt;RewriteCond&lt;/code&gt; evaluates to true, the next &lt;code&gt;RewriteRule&lt;/code&gt; will be considered. If it doesn't, the rule will be discarded. Multiple &lt;code&gt;RewriteConds&lt;/code&gt; may be used one after another, though all must evaluate to true for the next rule to be considered.&lt;/p&gt;

&lt;p&gt;As an example, let's assume you would like to redirect all requests to non-existent files or directories on your site back to the home page instead of showing the standard &lt;strong&gt;404 Not Found&lt;/strong&gt; error page. This can be achieved with following conditions rules:&lt;/p&gt;
&lt;div class="code-label " title="Redirect all requests to non-existent files and directories to home page"&gt;Redirect all requests to non-existent files and directories to home page&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With the above:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;%{REQUEST_FILENAME}&lt;/code&gt; is the string to check. In this case, it's the requested filename, which is a system variable available for every request.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-f&lt;/code&gt; is a built-in condition which verifies if the requested name exists on disk and is a file. The &lt;code&gt;!&lt;/code&gt; is a negation operator. Combined, &lt;code&gt;!-f&lt;/code&gt; evaluates to true only if a specified name does not exist or is not a file.&lt;/li&gt;
&lt;li&gt;Similarly, &lt;code&gt;!-d&lt;/code&gt; evaluates to true only if a specified name does not exist or is not a directory.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;RewriteRule&lt;/code&gt; on the final line will come into effect only for requests to non-existent files or directories. The &lt;code&gt;RewriteRule&lt;/code&gt; itself is very simple and redirects every request to the &lt;code&gt;/&lt;/code&gt; website root.&lt;/p&gt;

&lt;h1 id="conclusion"&gt;Conclusion&lt;/h1&gt;

&lt;p&gt;&lt;code&gt;mod_rewrite&lt;/code&gt; lets you create human-readable URLs. In this tutorial, you learned how to use the &lt;code&gt;RewriteRule&lt;/code&gt; directive to redirect URLs, including ones with query strings. You also learned how to conditionally redirect URLs using the &lt;code&gt;RewriteCond&lt;/code&gt; directive.&lt;/p&gt;

&lt;p&gt;If you'd like to learn more about &lt;code&gt;mod_rewrite&lt;/code&gt;, take a look at &lt;a href="http://httpd.apache.org/docs/current/rewrite/intro.html"&gt;Apache's mod_rewrite Introduction&lt;/a&gt; and &lt;a href="http://httpd.apache.org/docs/current/mod/mod_rewrite.html"&gt;Apache's official documentation for mod_rewrite&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-install-and-use-composer-on-debian-9</id>
    <published>2018-09-07T16:09:10Z</published>
    <updated>2018-09-07T16:12:25Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-install-and-use-composer-on-debian-9"/>
    <title>How To Install and Use Composer on Debian 9</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://getcomposer.org"&gt;Composer&lt;/a&gt; is a popular &lt;em&gt;dependency management&lt;/em&gt; tool for PHP, created mainly to facilitate installation and updates for project dependencies. It will check which other packages a specific project depends on and install them for you, using the appropriate versions according to the project requirements.&lt;/p&gt;

&lt;p&gt;In this tutorial, you'll install and get started with Composer on Debian 9.&lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;To complete this tutorial, you will need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One Debian 9 server set up by following &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-debian-9"&gt;the Debian 9 initial server setup guide&lt;/a&gt;, including a non-root user with &lt;code&gt;sudo&lt;/code&gt; access and a firewall.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="step-1-—-installing-the-dependencies"&gt;Step 1 — Installing the Dependencies&lt;/h2&gt;

&lt;p&gt;Before you download and install Composer, ensure your server has all dependencies installed.&lt;/p&gt;

&lt;p&gt;First, update the package manager cache by running:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt update
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, let's install the dependencies. We'll need &lt;code&gt;curl&lt;/code&gt; in order to download Composer and &lt;code&gt;php-cli&lt;/code&gt; for installing and running it. The &lt;code&gt;php-mbstring&lt;/code&gt; package is necessary to provide functions for a library we'll be using. &lt;code&gt;git&lt;/code&gt; is used by Composer for downloading project dependencies, and &lt;code&gt;unzip&lt;/code&gt; for extracting zipped packages. Everything can be installed with the following command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install curl php-cli php-mbstring git unzip
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With the prerequisites installed, we can install Composer itself.&lt;/p&gt;

&lt;h2 id="step-2-—-downloading-and-installing-composer"&gt;Step 2 — Downloading and Installing Composer&lt;/h2&gt;

&lt;p&gt;Composer provides an &lt;a href="https://getcomposer.org/installe"&gt;installer&lt;/a&gt;, written in PHP.  We'll download it, verify that it's not corrupted, and then use it to install Composer.&lt;/p&gt;

&lt;p&gt;Make sure you're in your home directory, then retrieve the installer using &lt;code&gt;curl&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cd ~
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;curl -sS https://getcomposer.org/installer -o composer-setup.php
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, verify that the installer matches the SHA-384 hash for the latest installer found on the [Composer Public Keys / Signatures][composer-sigs] page.  Copy the hash from that page and store it as a shell variable:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;HASH=&lt;span class="highlight"&gt;544e09ee996cdf60ece3804abc52599c22b1f40f4323403c44d44fdfdd586475ca9813a858088ffbc1f233e9b180f061&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Make sure that you substitute the latest hash for the highlighted value.&lt;/p&gt;

&lt;p&gt;Now execute the following PHP script to verify that the installation script is safe to run:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;php -r "if (hash_file('SHA384', 'composer-setup.php') === '$HASH') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You'll see the following output.&lt;/p&gt;
&lt;div class="code-label " title="Output"&gt;Output&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;Installer verified
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you see &lt;code&gt;Installer corrupt&lt;/code&gt;, then you'll need to redownload the installation script again and double check that you're using the correct hash.  Then run the command to verify the installer again. Once you have a verified installer, you can continue.&lt;/p&gt;

&lt;p&gt;To install &lt;code&gt;composer&lt;/code&gt; globally, use the following command which will download and install Composer as a system-wide command named &lt;code&gt;composer&lt;/code&gt;, under &lt;code&gt;/usr/local/bin&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo php composer-setup.php --install-dir=&lt;span class="highlight"&gt;/usr/local/bin&lt;/span&gt; --filename=&lt;span class="highlight"&gt;composer&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You'll see the following output:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;All settings correct for using Composer
Downloading...

Composer (version 1.7.2) successfully installed to: /usr/local/bin/composer
Use it: php /usr/local/bin/composer
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To test your installation, run:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;composer
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And you'll see this output displaying Composer's version and arguments.&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;   ______
  / ____/___  ____ ___  ____  ____  ________  _____
 / /   / __ \/ __ `__ \/ __ \/ __ \/ ___/ _ \/ ___/
/ /___/ /_/ / / / / / / /_/ / /_/ (__  )  __/ /
\____/\____/_/ /_/ /_/ .___/\____/____/\___/_/
                    /_/
Composer version 1.7.2 2018-08-16 16:57:12

Usage:
  command [options] [arguments]

Options:
  -h, --help                     Display this help message
  -q, --quiet                    Do not output any message
  -V, --version                  Display this application version
      --ansi                     Force ANSI output
      --no-ansi                  Disable ANSI output
  -n, --no-interaction           Do not ask any interactive question
      --profile                  Display timing and memory usage information
      --no-plugins               Whether to disable plugins.
  -d, --working-dir=WORKING-DIR  If specified, use the given directory as working directory.
  -v|vv|vvv, --verbose           Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This verifies that Composer installed successfully on your system and is available system-wide.&lt;/p&gt;

&lt;span class='note'&gt;&lt;p&gt;
&lt;strong&gt;Note:&lt;/strong&gt; If you prefer to have separate Composer executables for each project you host on this server, you can  install it locally, on a per-project basis.  Users of NPM will be familiar with this approach.  This method is also useful when your system user doesn't have permission to install software system-wide.&lt;/p&gt;

&lt;p&gt;To do this, use the command &lt;code&gt;php composer-setup.php&lt;/code&gt;.  This will generate a &lt;code&gt;composer.phar&lt;/code&gt; file in your current directory, which can be executed with &lt;code&gt;./composer.phar &lt;span class="highlight"&gt;command&lt;/span&gt;&lt;/code&gt;.&lt;br&gt;&lt;/p&gt;&lt;/span&gt;

&lt;p&gt;Now let's look at using Composer to manage dependencies.&lt;/p&gt;

&lt;h2 id="step-3-—-using-composer-in-a-php-project"&gt;Step 3 — Using Composer in a PHP Project&lt;/h2&gt;

&lt;p&gt;PHP projects often depend on external libraries, and managing those dependencies and their versions can be tricky. Composer solves that by tracking your dependencies and making it easy for others to install them.&lt;/p&gt;

&lt;p&gt;In order to use Composer in your project, you'll need a &lt;code&gt;composer.json&lt;/code&gt; file. The &lt;code&gt;composer.json&lt;/code&gt; file  tells Composer which dependencies it needs to download for your project, and which versions of each package are allowed to be installed. This is extremely important to keep your project consistent and avoid installing unstable versions that could potentially cause backwards compatibility issues.&lt;/p&gt;

&lt;p&gt;You don't need to create this file manually - it's easy to run into syntax errors when you do so. Composer auto-generates the &lt;code&gt;composer.json&lt;/code&gt; file when you add a dependency to your project using the &lt;code&gt;require&lt;/code&gt; command. You can  add additional dependencies  in the same way, without the need to manually edit this file.&lt;/p&gt;

&lt;p&gt;The process of using Composer to install a package as dependency in a project  involves the following steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Identify what kind of library the application needs.&lt;/li&gt;
&lt;li&gt;Research a suitable open source library on &lt;a href="https://packagist.org/"&gt;Packagist.org&lt;/a&gt;, the official package repository for Composer.&lt;/li&gt;
&lt;li&gt;Choose the package you want to depend on.&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;composer require&lt;/code&gt; to include the dependency in the &lt;code&gt;composer.json&lt;/code&gt; file and install the package.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's try this out with a demo application.&lt;/p&gt;

&lt;p&gt;The goal of this application is to transform a given sentence into a URL-friendly string - a &lt;em&gt;slug&lt;/em&gt;. This is commonly used to convert page titles to URL paths (like the final portion of the URL for this tutorial).&lt;/p&gt;

&lt;p&gt;Let's start by creating a directory for our project. We'll call it &lt;strong&gt;slugify&lt;/strong&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cd ~
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;mkdir slugify
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;cd slugify
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now it's time to search &lt;a href="https://packagist.org/"&gt;Packagist.org&lt;/a&gt; for a package that can help us generate &lt;em&gt;slugs&lt;/em&gt;. If you search for the term "slug" on Packagist, you'll get a result similar to this:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/composer_1404/packagist.png" alt="Packagist Search: easy-slug/easy-slug, muffin/slug, ddd/slug, zelenin/slug, webcastle/slug, anomaly/slug-field_type"&gt;&lt;/p&gt;

&lt;p&gt;You'll see two numbers on the right side of each package in the list. The number on the top represents how many times the package was installed, and the number on the bottom shows how many times a package was starred on &lt;a href="https://github.com"&gt;GitHub&lt;/a&gt;. You can reorder the search results based on these numbers (look for the two icons on the right side of the search bar). Generally speaking, packages with more installations and more stars tend to be more stable, since so many people are using them. It's also important to check the package description for relevance to make sure it's what you need.&lt;/p&gt;

&lt;p&gt;We need a simple string-to-slug converter. From the search results, the package &lt;code&gt;cocur/slugify&lt;/code&gt; seems to be a good match, with a reasonable amount of installations and stars. (The package is a bit further down the page than the screenshot shows.)&lt;/p&gt;

&lt;p&gt;Packages on Packagist have a &lt;strong&gt;vendor&lt;/strong&gt; name and a &lt;strong&gt;package&lt;/strong&gt; name. Each package has a unique identifier (a namespace) in the same format GitHub uses for its repositories, in the form &lt;code&gt;&lt;span class="highlight"&gt;vendor&lt;/span&gt;/&lt;span class="highlight"&gt;package&lt;/span&gt;&lt;/code&gt;. The library we want to install uses the namespace &lt;code&gt;cocur/slugif&lt;/code&gt;. You need the namespace in order to require the package in your project.&lt;/p&gt;

&lt;p&gt;Now that you know exactly which package you want to install, run &lt;code&gt;composer require&lt;/code&gt; to include it as a dependency and also generate the &lt;code&gt;composer.json&lt;/code&gt; file for the project:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;composer require cocur/slugify
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You'll see this output as Composer downloads the dependency:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Using version &lt;span class="highlight"&gt;^3.1&lt;/span&gt; for &lt;span class="highlight"&gt;cocur/slugify&lt;/span&gt;
&lt;span class="highlight"&gt;./composer.json&lt;/span&gt; has been created
Loading composer repositories with package information
Updating dependencies (including require-dev)
Package operations: 1 install, 0 updates, 0 removals
  - &lt;span class="highlight"&gt;Installing cocur/slugify (v3.1)&lt;/span&gt;: Downloading (100%)
Writing lock file
Generating autoload files
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see from the output, Composer automatically decided which version of the package to use. If you check your project's directory now, it will contain two new files: &lt;code&gt;composer.json&lt;/code&gt; and &lt;code&gt;composer.lock&lt;/code&gt;, and a &lt;code&gt;vendor&lt;/code&gt; directory:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;ls -l
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;total 12
-rw-r--r-- 1 sammy sammy   59 Sep  7 16:03 composer.json
-rw-r--r-- 1 sammy sammy 2934 Sep  7 16:03 composer.lock
drwxr-xr-x 4 sammy sammy 4096 Sep  7 16:03 vendor
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;composer.lock&lt;/code&gt; file is used to store information about which versions of each package are installed, and ensure the same versions are used if someone else clones your project and installs its dependencies. The &lt;code&gt;vendor&lt;/code&gt; directory is where the project dependencies are located. The &lt;code&gt;vendor&lt;/code&gt; folder doesn't need to be committed into version control - you only need to include the &lt;strong&gt;composer.json&lt;/strong&gt; and &lt;strong&gt;composer.lock&lt;/strong&gt; files. &lt;/p&gt;

&lt;p&gt;&lt;span class='note'&gt;When installing a project that already contains a &lt;code&gt;composer.json&lt;/code&gt; file, run &lt;code&gt;composer install&lt;/code&gt; in order to download the project's dependencies.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;Let's take a quick look at version constraints. If you check the contents of your &lt;code&gt;composer.json&lt;/code&gt; file, you'll see something like this:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cat composer.json
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-json"&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;{
    "require": {
        "cocur/slugify": "^3.1"
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You might notice the special character &lt;code&gt;^&lt;/code&gt; before the version number in &lt;code&gt;composer.json&lt;/code&gt;. Composer supports several different constraints and formats for defining the required package version, in order to provide flexibility while also keeping your project stable. The caret (&lt;code&gt;^&lt;/code&gt;) operator used by the auto-generated &lt;code&gt;composer.json&lt;/code&gt; file is the recommended operator for maximum interoperability, following &lt;a href="http://semver.org/"&gt;semantic versioning&lt;/a&gt;. In this case, it defines &lt;strong&gt;3.1&lt;/strong&gt; as the minimum compatible version, and allows updates to any future version below &lt;strong&gt;4.0&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Generally speaking, you won't need to tamper with version constraints in your &lt;code&gt;composer.json&lt;/code&gt; file. However, some situations might require that you manually edit the constraints–for instance, when a major new version of your required library is released and you want to upgrade, or when the library you want to use doesn't follow semantic versioning.&lt;/p&gt;

&lt;p&gt;Here are some examples to give you a better understanding of how Composer version constraints work:&lt;/p&gt;

&lt;table&gt;&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Constraint&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;th&gt;Example Versions Allowed&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;^1.0&lt;/td&gt;
&lt;td&gt;&amp;gt;= 1.0 &amp;lt; 2.0&lt;/td&gt;
&lt;td&gt;1.0, 1.2.3, 1.9.9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;^1.1.0&lt;/td&gt;
&lt;td&gt;&amp;gt;= 1.1.0 &amp;lt; 2.0&lt;/td&gt;
&lt;td&gt;1.1.0, 1.5.6, 1.9.9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;~1.0&lt;/td&gt;
&lt;td&gt;&amp;gt;= 1.0 &amp;lt; 2.0.0&lt;/td&gt;
&lt;td&gt;1.0, 1.4.1, 1.9.9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;~1.0.0&lt;/td&gt;
&lt;td&gt;&amp;gt;= 1.0.0 &amp;lt; 1.1&lt;/td&gt;
&lt;td&gt;1.0.0, 1.0.4, 1.0.9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1.2.1&lt;/td&gt;
&lt;td&gt;1.2.1&lt;/td&gt;
&lt;td&gt;1.2.1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1.*&lt;/td&gt;
&lt;td&gt;&amp;gt;= 1.0 &amp;lt; 2.0&lt;/td&gt;
&lt;td&gt;1.0.0, 1.4.5, 1.9.9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1.2.*&lt;/td&gt;
&lt;td&gt;&amp;gt;= 1.2 &amp;lt; 1.3&lt;/td&gt;
&lt;td&gt;1.2.0, 1.2.3, 1.2.9&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;

&lt;p&gt;For a more in-depth view of Composer version constraints, see &lt;a href="https://getcomposer.org/doc/articles/versions.md"&gt;the official documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Next, let's look at how to load dependencies automatically with Composer.&lt;/p&gt;

&lt;h2 id="step-4-—-including-the-autoload-script"&gt;Step 4 — Including the Autoload Script&lt;/h2&gt;

&lt;p&gt;Since PHP itself doesn't automatically load classes, Composer provides an autoload script that you can include in your project to get autoloading for free. This makes it much easier to work with your dependencies.&lt;/p&gt;

&lt;p&gt;The only thing you need to do is include the &lt;code&gt;vendor/autoload.php&lt;/code&gt; file in your PHP scripts before any class instantiation. This file is automatically generated by Composer when you add your first dependency.&lt;/p&gt;

&lt;p&gt;Let's try it out in our application. Create the file &lt;code&gt;test.php&lt;/code&gt; and open it in your text editor:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nano test.php
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add the following code which brings in the &lt;code&gt;vendor/autoload.php&lt;/code&gt; file, loads the  &lt;code&gt;cocur/slugify&lt;/code&gt; dependency, and uses it to create a slug:&lt;/p&gt;
&lt;div class="code-label " title="test.php"&gt;test.php&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-php"&gt;&amp;lt;?php require __DIR__ . '/vendor/autoload.php'; 
use Cocur\Slugify\Slugify;

$slugify = new Slugify();

echo $slugify-&amp;gt;slugify('Hello World, this is a long sentence and I need to make a slug from it!');
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save the file and exit your editor.&lt;/p&gt;

&lt;p&gt;Now run the script:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;php test.php
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This  produces the output &lt;code&gt;hello-world-this-is-a-long-sentence-and-i-need-to-make-a-slug-from-it&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Dependencies need updates when new versions come out, so let's look at how to handle that.&lt;/p&gt;

&lt;h2 id="step-5-—-updating-project-dependencies"&gt;Step 5 — Updating Project Dependencies&lt;/h2&gt;

&lt;p&gt;Whenever you want to update your project dependencies to more recent versions, run the &lt;code&gt;update&lt;/code&gt; command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;composer update
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will check for newer versions of the libraries you required in your project. If a newer version is found and it's compatible with the version constraint defined in the &lt;code&gt;composer.json&lt;/code&gt; file, Composer will replace the previous version installed. The &lt;code&gt;composer.lock&lt;/code&gt; file will be updated to reflect these changes.&lt;/p&gt;

&lt;p&gt;You can also update one or more specific libraries by specifying them like this:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;composer update &lt;span class="highlight"&gt;vendor/package vendor2/package2&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Be sure to check in your &lt;code&gt;composer.json&lt;/code&gt; and &lt;code&gt;composer.lock&lt;/code&gt; files after you update your dependencies so that others can install these newer versions.&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Composer is a powerful tool every PHP developer should have in their utility belt. In this tutorial you installed Composer on Debian 9 and used it in a simple project. You now know how to install and update dependencies.&lt;/p&gt;

&lt;p&gt;Beyond providing an easy and reliable way for managing project dependencies, it also establishes a new de facto standard for sharing and discovering PHP packages created by the community.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-install-and-configure-owncloud-on-debian-9</id>
    <published>2018-09-07T15:58:15Z</published>
    <updated>2018-09-07T15:58:52Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-install-and-configure-owncloud-on-debian-9"/>
    <title>How To Install and Configure ownCloud on Debian 9</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;ownCloud is an open-source file sharing server and collaboration platform that can store your personal content, like documents and pictures, in a centralized location.  This allows you to take control of your content and security by not relying on third-party content hosting services like Dropbox.&lt;/p&gt;

&lt;p&gt;In this tutorial, we will install and configure an ownCloud instance on a Debian 9 server.&lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;In order to complete the steps in this guide, you will need the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;A sudo user and firewall on your server&lt;/strong&gt;: You can create a user with &lt;code&gt;sudo&lt;/code&gt; privileges and set up a basic firewall by following the &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-debian-9"&gt;Debian 9 initial server setup guide&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A LAMP stack&lt;/strong&gt;: ownCloud requires a web server, a database, and PHP to function properly.  Setting up a LAMP stack (Linux, Apache, MySQL, and PHP) server fulfills all of these requirements.  Follow &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-linux-apache-mariadb-php-lamp-stack-debian9"&gt;this guide&lt;/a&gt; to install and configure this software.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;An SSL certificate&lt;/strong&gt;: How you set this up depends on whether or not you have a domain name that resolves to your server.

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;If you have a domain name...&lt;/strong&gt; the easiest way to secure your site is with Let's Encrypt, which provides free, trusted certificates. Follow the &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-secure-apache-with-let-s-encrypt-on-debian-9"&gt;Let's Encrypt guide for Apache&lt;/a&gt; to set this up.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;If you do not have a domain...&lt;/strong&gt; and you are just using this configuration for testing or personal use, you can use a self-signed certificate instead. This provides the same type of encryption, but without the domain validation. Follow the &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-create-a-self-signed-ssl-certificate-for-apache-in-debian-9"&gt;self-signed SSL guide for Apache&lt;/a&gt; to get set up.&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="step-1-–-installing-owncloud"&gt;Step 1 – Installing ownCloud&lt;/h2&gt;

&lt;p&gt;The ownCloud server package does not exist within the default repositories for Debian.  However, ownCloud maintains a dedicated repository for the distribution that we can add to our server.&lt;/p&gt;

&lt;p&gt;To begin, let's install a few components to help us add the ownCloud repositories.  The &lt;code&gt;apt-transport-https&lt;/code&gt; package allows us to use the &lt;code&gt;deb https://&lt;/code&gt; in our &lt;code&gt;apt&lt;/code&gt; sources list to indicate external repositories served over HTTPS:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt update
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo apt install curl apt-transport-https
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, download the ownCloud release key using the &lt;code&gt;curl&lt;/code&gt; command and import it with the &lt;code&gt;apt-key&lt;/code&gt; utility with the &lt;code&gt;add&lt;/code&gt; command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;curl https://download.owncloud.org/download/repositories/production/Debian_9.0/Release.key | sudo apt-key add -
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The 'Release.key' file contains a PGP (Pretty Good Privacy) public key which &lt;code&gt;apt&lt;/code&gt; will use to verify that the ownCloud package is authentic.&lt;/p&gt;

&lt;p&gt;In addition to importing the key, create a file called &lt;code&gt;owncloud.list&lt;/code&gt; in the &lt;code&gt;sources.list.d&lt;/code&gt; directory for &lt;code&gt;apt&lt;/code&gt;. The file will contain the address to the ownCloud repository.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;echo 'deb http://download.owncloud.org/download/repositories/production/Debian_9.0/ /' | sudo tee /etc/apt/sources.list.d/owncloud.list
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, we can use the package manager to find and install ownCloud.  Along with the main package, we will also install a few additional PHP libraries that ownCloud uses to add extra functionality.  Update your local package index and install everything by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt update
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo apt install php-bz2 php-curl php-gd php-imagick php-intl php-mbstring php-xml php-zip owncloud-files
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Everything we need is now installed on the server, so next we can finish the configuration and we can begin using the service.&lt;/p&gt;

&lt;h2 id="step-2-—-adjusting-the-document-root"&gt;Step 2 — Adjusting the Document Root&lt;/h2&gt;

&lt;p&gt;The ownCloud package we installed copies the web files to &lt;code&gt;/var/www/owncloud&lt;/code&gt; on the server.  Currently, the Apache virtual host configuration is set up to serve files out of a different directory.  We need to change the &lt;code&gt;DocumentRoot&lt;/code&gt; setting in our configuration to point to the new directory.&lt;/p&gt;

&lt;p&gt;You find which virtual host files reference your domain name or IP address using the &lt;code&gt;apache2ctl&lt;/code&gt; utility with the &lt;code&gt;DUMP_VHOSTS&lt;/code&gt; option.  Filter the output by your server's domain name or IP address to find which files you need to edit in the next few commands:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apache2ctl -t -D DUMP_VHOSTS | grep &lt;span class="highlight"&gt;server_domain_or_IP&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The output will probably look something like this:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;*:443                  &lt;span class="highlight"&gt;server_domain_or_IP&lt;/span&gt; (/etc/apache2/sites-enabled/&lt;span class="highlight"&gt;server_domain_or_IP&lt;/span&gt;-le-ssl.conf:2)
         port 80 namevhost &lt;span class="highlight"&gt;server_domain_or_IP&lt;/span&gt; (/etc/apache2/sites-enabled/&lt;span class="highlight"&gt;server_domain_or_IP&lt;/span&gt;.conf:1)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the parentheses, you can see each of the files that reference the domain name or IP address we'll use to access ownCloud.  These are the files you'll need to edit.&lt;/p&gt;

&lt;p&gt;For each match, open the file in a text editor with &lt;code&gt;sudo&lt;/code&gt; privileges:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/apache2/sites-enabled/&lt;span class="highlight"&gt;server_domain_or_IP&lt;/span&gt;.conf
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Inside, search for the &lt;code&gt;DocumentRoot&lt;/code&gt; directive.  Change the line so that it points to the &lt;code&gt;/var/www/owncloud&lt;/code&gt; directory:&lt;/p&gt;
&lt;div class="code-label " title="Example DocumentRoot edit"&gt;Example DocumentRoot edit&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&amp;lt;VirtualHost *:80&amp;gt;
    . . .
    DocumentRoot &lt;span class="highlight"&gt;/var/www/owncloud&lt;/span&gt;
    . . .
&amp;lt;/VirtualHost&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save and close the file when you are finished.  Complete this process for each of the files that referenced your domain name (or IP address if you did not configure a domain for your server).&lt;/p&gt;

&lt;p&gt;When you are finished, check the syntax of your Apache files to make sure there were no detectable typos in your configuration:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apache2ctl configtest
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Syntax OK
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Depending on your configuration, you may see a warning about setting &lt;code&gt;ServerName&lt;/code&gt; globally.  As long as the output ends with &lt;code&gt;Syntax OK&lt;/code&gt;, you can ignore that warning.  If you see additional errors, go back and check the files you just edited for mistakes.&lt;/p&gt;

&lt;p&gt;If your syntax check passed, reload the Apache service to activate the new changes:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl reload apache2
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Apache should now know how to server your ownCloud files.&lt;/p&gt;

&lt;h2 id="step-3-–-configuring-the-mysql-database"&gt;Step 3 – Configuring the MySQL Database&lt;/h2&gt;

&lt;p&gt;Before we move on to the web configuration, we need to set up the database.  During the web-based configuration process, we will need to provide an database name, a database username, and a database password so that ownCloud can connect and manage its information within MySQL.&lt;/p&gt;

&lt;p&gt;Begin by logging into your database with the MySQL administrative account:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo mysql
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you set up password authentication for a MySQL administrative account, you may have to use this syntax instead:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;mysql -u &lt;span class="highlight"&gt;admin&lt;/span&gt; -p
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Create a dedicated database for ownCloud to use.  We will name the database &lt;code&gt;owncloud&lt;/code&gt; for clarity:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;CREATE DATABASE owncloud;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span class='note'&gt;&lt;strong&gt;Note:&lt;/strong&gt; Every MySQL statement must end with a semi-colon (;). Be sure to check that this is present if you are experiencing an issue.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;Next, create a separate MySQL user account to manage the newly created database.  Creating one-function databases and accounts is a good idea from a management and security standpoint.  As with the naming of the database, choose a username that you prefer.  We elected to go with the name &lt;code&gt;owncloud&lt;/code&gt; in this guide.&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;GRANT ALL ON owncloud.* to 'owncloud'@'localhost' IDENTIFIED BY '&lt;span class="highlight"&gt;owncloud_database_password&lt;/span&gt;';
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span class='warning'&gt;&lt;strong&gt;Warning:&lt;/strong&gt; Be sure to put an actual password where the command states: &lt;code&gt;&lt;span class="highlight"&gt;owncloud_database_password&lt;/span&gt;&lt;/code&gt;&lt;br&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;With the user assigned access to the database, perform the flush privileges operation to ensure that the running instance of MySQL knows about the recent privilege assignment:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;FLUSH PRIVILEGES;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can now exit the MySQL session by typing:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;exit
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With the ownCloud server installed and the database set up, we are ready to turn our attention to configuring the ownCloud application.&lt;/p&gt;

&lt;h2 id="step-4-–-configuring-owncloud"&gt;Step 4 – Configuring ownCloud&lt;/h2&gt;

&lt;p&gt;To access the ownCloud web interface, open a web browser and navigate to the following address:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;https://&lt;span class="highlight"&gt;server_domain_or_IP&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span class='note'&gt;&lt;strong&gt;Note:&lt;/strong&gt; If you are using a self-signed SSL certificate, you will likely be presented with a warning because the certificate is not signed by one of your browser's trusted authorities.  This is expected and normal.  Click the appropriate button or link to proceed to the ownCloud admin page.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;You should see the ownCloud web configuration page in your browser.&lt;/p&gt;

&lt;p&gt;Create an admin account by choosing a username and a password.  For security purposes it is not recommended to use something like "admin" for the username:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/owncloud_install_ubuntu_1804/admin_user.png" alt="ownCloud Admin Account"&gt;&lt;/p&gt;

&lt;p&gt;Next, leave the &lt;strong&gt;Data folder&lt;/strong&gt; setting as-is and scroll down to the database configuration section.&lt;/p&gt;

&lt;p&gt;Fill out the details of the database name, database username, and database password you created in the previous section.  If you used the settings from this guide, both the database name and username will be &lt;code&gt;owncloud&lt;/code&gt;.  Leave the database host as &lt;code&gt;localhost&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/owncloud_install_ubuntu_1804/db_configure.png" alt="ownCloud database configuration"&gt;&lt;/p&gt;

&lt;p&gt;Click the &lt;strong&gt;Finish setup&lt;/strong&gt; button to finish configuring ownCloud using the information you've provided.  You will be taken to a login screen where you can sign in using your new account:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/owncloud_install_ubuntu_1804/login_screen.png" alt="ownCloud login screen"&gt;&lt;/p&gt;

&lt;p&gt;On your first login, a screen will appear where you can download applications to sync your files on various devices.  You can download and configure these now or do it at a later time.  When you are finished, click the &lt;strong&gt;x&lt;/strong&gt; in the top-right corner of the splash screen to access the main interface:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/owncloud_install_ubuntu_1804/main_interface.png" alt="ownCloud Main Interface"&gt;&lt;/p&gt;

&lt;p&gt;Here, you can create or upload files to your personal cloud.&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;ownCloud can replicate the capabilities of popular third-party cloud storage services.  Content can be shared between users or externally with public URLs.  The advantage of ownCloud is that the information is stored in a place that you control and manage without a third party.&lt;/p&gt;

&lt;p&gt;Explore the interface and for additional functionality, install plugins using &lt;a href="https://apps.owncloud.com/"&gt;ownCloud's app store&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-install-wordpress-with-lamp-on-debian-9</id>
    <published>2018-09-07T15:07:57Z</published>
    <updated>2018-09-07T15:36:09Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-install-wordpress-with-lamp-on-debian-9"/>
    <title>How To Install WordPress with LAMP on Debian 9</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;WordPress is the most popular CMS (content management system) on the internet. It allows you to easily set up flexible blogs and websites on top of a MariaDB backend with PHP processing. WordPress has seen incredible adoption and is a great choice for getting a website up and running quickly. After setup, almost all administration can be done through the web frontend.&lt;/p&gt;

&lt;p&gt;In this guide, we'll focus on getting a WordPress instance set up on a LAMP stack (Linux, Apache, MariaDB, and PHP) on a Debian 9 server.&lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;In order to complete this tutorial, you will need access to a Debian 9 server.&lt;/p&gt;

&lt;p&gt;You will need to perform the following tasks before you can start this guide:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Create a &lt;code&gt;sudo&lt;/code&gt; user on your server&lt;/strong&gt;: We will be completing the steps in this guide using a non-&lt;strong&gt;root&lt;/strong&gt; user with &lt;code&gt;sudo&lt;/code&gt; privileges. You can create a user with &lt;code&gt;sudo&lt;/code&gt; privileges by following our &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-18-04"&gt;Debian 9 initial server setup guide&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Install a LAMP stack&lt;/strong&gt;: WordPress will need a web server, a database, and PHP in order to correctly function. Setting up a LAMP stack (Linux, Apache, MariaDB, and PHP) fulfills all of these requirements. Follow &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-linux-apache-mariadb-php-lamp-stack-debian9"&gt;this guide&lt;/a&gt; to install and configure this software.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Secure your site with SSL&lt;/strong&gt;: WordPress serves dynamic content and handles user authentication and authorization. TLS/SSL is the technology that allows you to encrypt the traffic from your site so that your connection is secure. The way you set up SSL will depend on whether you have a domain name for your site.

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;If you have a domain name...&lt;/strong&gt; the easiest way to secure your site is with Let's Encrypt, which provides free, trusted certificates. Follow our &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-secure-apache-with-let-s-encrypt-on-debian-9"&gt;Let's Encrypt guide for Apache&lt;/a&gt; to set this up.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;If you do not have a domain...&lt;/strong&gt; and you are just using this configuration for testing or personal use, you can use a self-signed certificate instead. This provides the same type of encryption, but without the domain validation. Follow our &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-create-a-self-signed-ssl-certificate-for-apache-in-debian-9"&gt;self-signed SSL guide for Apache&lt;/a&gt; to get set up.&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When you are finished with the setup steps, log in to your server as your &lt;code&gt;sudo&lt;/code&gt; user and continue below.&lt;/p&gt;

&lt;h2 id="step-1-—-creating-a-mariadb-database-and-user-for-wordpress"&gt;Step 1 — Creating a MariaDB Database and User for WordPress&lt;/h2&gt;

&lt;p&gt;The first step that we will take is a preparatory one. WordPress uses MySQL to manage and store site and user information. We have MariaDB — a drop-in replacement for MySQL — installed already, but we need to make a database and a user for WordPress to use.&lt;/p&gt;

&lt;p&gt;To get started, open up the MariaDB prompt as the &lt;strong&gt;root&lt;/strong&gt; account:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo mariadb
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;span class='note'&gt;&lt;p&gt;
&lt;strong&gt;Note:&lt;/strong&gt; If you set up another account with administrative privileges when you installed and set up MariaDB, you can also log in as that user. You’ll need to do so with the following command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;mariadb -u &lt;span class="highlight"&gt;username&lt;/span&gt; -p
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After issuing this command, MariaDB will prompt you for the password you set for that account.&lt;br&gt;&lt;/p&gt;&lt;/span&gt;

&lt;p&gt;Begin by creating a new database that WordPress will control. You can call this whatever you would like but, to keep it simple for this guide, we will name it &lt;strong&gt;wordpress&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Create the database for WordPress by typing:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;CREATE DATABASE &lt;span class="highlight"&gt;wordpress&lt;/span&gt; DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that every MySQL statement must end in a semi-colon (&lt;code&gt;;&lt;/code&gt;). Check to make sure this is present if you are running into any issues.&lt;/p&gt;

&lt;p&gt;Next, create a separate MySQL user account that we will use exclusively to operate on our new database. Creating single-function databases and accounts is a good idea from a management and security standpoint. We will use the name &lt;strong&gt;wordpressuser&lt;/strong&gt; in this guide, but feel free to change this if you'd like.&lt;/p&gt;

&lt;p&gt;Create this account, set a password, and grant the user access to the database you just created with the following command. Remember to choose a strong password for your database user:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="MariaDB [(none)]&amp;gt;"&gt;GRANT ALL ON &lt;span class="highlight"&gt;wordpress&lt;/span&gt;.* TO '&lt;span class="highlight"&gt;wordpressuser&lt;/span&gt;'@'localhost' IDENTIFIED BY '&lt;span class="highlight"&gt;password&lt;/span&gt;';
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You now have a database and user account, each made specifically for WordPress. Run the following command to reload the grant tables so that the current instance of MariaDB knows about the changes you've made:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="MariaDB [(none)]&amp;gt;"&gt;FLUSH PRIVILEGES;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Exit out of MariaDB by typing:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="MariaDB [(none)]&amp;gt;"&gt;EXIT;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that you’ve configured the database and user that will be used by WordPress, you can move on to installing some PHP-related packages used by the CMS.&lt;/p&gt;

&lt;h2 id="step-2-—-installing-additional-php-extensions"&gt;Step 2 — Installing Additional PHP Extensions&lt;/h2&gt;

&lt;p&gt;When setting up our LAMP stack, we only required a very minimal set of extensions in order to get PHP to communicate with MariaDB. WordPress and many of its plugins leverage additional PHP extensions.&lt;/p&gt;

&lt;p&gt;Download and install some of the most popular PHP extensions for use with WordPress by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt update
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo apt install php-curl php-gd php-mbstring php-xml php-xmlrpc php-soap php-intl php-zip
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span class='note'&gt;&lt;strong&gt;Note:&lt;/strong&gt; Each WordPress plugin has its own set of requirements. Some may require additional PHP packages to be installed. Check your plugin documentation to find its PHP requirements. If they are available, they can be installed with &lt;code&gt;apt&lt;/code&gt; as demonstrated above.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;We will restart Apache to load these new extensions in the next section. If you are returning here to install additional plugins, you can restart Apache now by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl restart apache2
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At this point, all that’s left to do before installing WordPress is to make some changes to your Apache configuration in order to allow the CMS to function smoothly.&lt;/p&gt;

&lt;h2 id="step-3-—-adjusting-apache-39-s-configuration-to-allow-for-htaccess-overrides-and-rewrites"&gt;Step 3 — Adjusting Apache's Configuration to Allow for .htaccess Overrides and Rewrites&lt;/h2&gt;

&lt;p&gt;With the additional PHP extensions installed and ready for use, the next thing to do is to make a few changes to your Apache configuration. Based on the prerequisite tutorials, you should have a configuration file for your site in the &lt;code&gt;/etc/apache2/sites-available/&lt;/code&gt; directory. We'll use &lt;code&gt;/etc/apache2/sites-available/wordpress.conf&lt;/code&gt; as an example here, but you should substitute the path to your configuration file where appropriate.&lt;/p&gt;

&lt;p&gt;Additionally, we will use &lt;code&gt;/var/www/wordpress&lt;/code&gt; as the root directory of our WordPress install. You should use the web root specified in your own configuration.&lt;/p&gt;

&lt;p&gt;&lt;span class='note'&gt;&lt;strong&gt;Note:&lt;/strong&gt; It's possible you are using the &lt;code&gt;000-default.conf&lt;/code&gt; default configuration (with &lt;code&gt;/var/www/html&lt;/code&gt; as your web root). This is fine to use if you're only going to host one website on this server. If not, it's best to split the necessary configuration into logical chunks, one file per site.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;Currently, the use of &lt;code&gt;.htaccess&lt;/code&gt; files is disabled. WordPress and many WordPress plugins use these files extensively for in-directory tweaks to the web server's behavior.&lt;/p&gt;

&lt;p&gt;Open the Apache configuration file for your website. Note that if you have an existing Apache configuration file for your website, this file’s name will be different:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/apache2/sites-available/&lt;span class="highlight"&gt;wordpress&lt;/span&gt;.conf
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To allow &lt;code&gt;.htaccess&lt;/code&gt; files, you’ll need to add a &lt;code&gt;Directory&lt;/code&gt; block pointing to your document root with an &lt;code&gt;AllowOverride&lt;/code&gt; directive within it. Add the following block of text inside the &lt;code&gt;VirtualHost&lt;/code&gt; block in your configuration file, being sure to use the correct web root directory:&lt;/p&gt;
&lt;div class="code-label " title="/etc/apache2/sites-available/wordpress.conf"&gt;/etc/apache2/sites-available/wordpress.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&amp;lt;Directory &lt;span class="highlight"&gt;/var/www/wordpress/&lt;/span&gt;&amp;gt;
    AllowOverride All
&amp;lt;/Directory&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When you are finished, save and close the file.&lt;/p&gt;

&lt;p&gt;Next, enable the &lt;code&gt;rewrite&lt;/code&gt; module in order to utilize the WordPress permalink feature:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo a2enmod rewrite
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Before implementing the changes you've made, check to make sure that you haven't made any syntax errors:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apache2ctl configtest
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If your configuration file’s syntax is correct, you’ll see the following in your output:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Syntax OK
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If this command reports any errors, go back and check that you haven’t made any syntax errors in your configuration file. Otherwise, restart Apache to implement the changes:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl restart apache2
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, we will download and set up WordPress itself.&lt;/p&gt;

&lt;h2 id="step-4-—-downloading-wordpress"&gt;Step 4 — Downloading WordPress&lt;/h2&gt;

&lt;p&gt;Now that your server software is configured, you can download and set up WordPress. For security reasons in particular, it is always recommended to get the latest version of WordPress directly from their site.&lt;/p&gt;

&lt;span class='note'&gt;&lt;p&gt;
&lt;strong&gt;Note:&lt;/strong&gt; We will use &lt;code&gt;curl&lt;/code&gt; to download WordPress, but this program may not be installed by default on your Debian server. To install it, run:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install curl
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;/p&gt;&lt;/span&gt;

&lt;p&gt;Change into a writable directory and then download the compressed release by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cd /tmp
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;curl -O https://wordpress.org/latest.tar.gz
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Extract the compressed file to create the WordPress directory structure:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;tar xzvf latest.tar.gz
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We will move these files into our document root momentarily. Before we do, though, add a dummy &lt;code&gt;.htaccess&lt;/code&gt; file so that this will be available for WordPress to use later.&lt;/p&gt;

&lt;p&gt;Create the file by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;touch /tmp/wordpress/.htaccess
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then copy over the sample configuration file to the filename that WordPress actually reads:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cp /tmp/wordpress/wp-config-sample.php /tmp/wordpress/wp-config.php
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Additionally, create the &lt;code&gt;upgrade&lt;/code&gt; directory so that WordPress won't run into permissions issues when trying to do this on its own following an update to its software:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;mkdir /tmp/wordpress/wp-content/upgrade
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, copy the entire contents of the directory into your document root. Notice that the following command includes a dot at the end of the source directory to indicate that everything within the directory should be copied, including hidden files (like the &lt;code&gt;.htaccess&lt;/code&gt; file you created):&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo cp -a /tmp/wordpress/. /var/www/&lt;span class="highlight"&gt;wordpress&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With that, you’ve successfully installed WordPress onto your web server and performed some of the initial configuration steps. Next, we’ll discuss some further configuration changes that will give WordPress the privileges it needs to function as well as access to the MariaDB database and user account you created previously.&lt;/p&gt;

&lt;h2 id="step-5-—-configuring-the-wordpress-directory"&gt;Step 5 — Configuring the WordPress Directory&lt;/h2&gt;

&lt;p&gt;Before we can go through the web-based setup process for WordPress, we need to adjust some items in our WordPress directory.&lt;/p&gt;

&lt;p&gt;Start by giving ownership of all the files to the &lt;strong&gt;www-data&lt;/strong&gt; user and group. This is the user that the Apache web server runs as, and Apache will need to be able to read and write WordPress files in order to serve the website and perform automatic updates.&lt;/p&gt;

&lt;p&gt;Update the ownership with &lt;code&gt;chown&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo chown -R www-data:www-data /var/www/&lt;span class="highlight"&gt;wordpress&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next we will run two &lt;code&gt;find&lt;/code&gt; commands to set the correct permissions on the WordPress directories and files:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo find /var/www/&lt;span class="highlight"&gt;wordpress&lt;/span&gt;/ -type d -exec chmod 750 {} \;
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo find /var/www/&lt;span class="highlight"&gt;wordpress&lt;/span&gt;/ -type f -exec chmod 640 {} \;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These should be a reasonable permissions set to start with, although some plugins and procedures might require additional tweaks.&lt;/p&gt;

&lt;p&gt;Following this, you will need to make some changes to the main WordPress configuration file. &lt;/p&gt;

&lt;p&gt;When you open the file, your first objective will be to adjust some secret keys to provide some security for your installation. WordPress provides a secure generator for these values so that you do not have to try to come up with good values on your own. These are only used internally, so it won't hurt usability to have complex, secure values here.&lt;/p&gt;

&lt;p&gt;To grab secure values from the WordPress secret key generator, type:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;curl -s https://api.wordpress.org/secret-key/1.1/salt/
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will get back unique values that look something like this:&lt;/p&gt;

&lt;p&gt;&lt;span class='warning'&gt;&lt;strong&gt;Warning!&lt;/strong&gt; It is important that you request unique values each time. Do &lt;strong&gt;NOT&lt;/strong&gt; copy the values shown below!&lt;br&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;define('AUTH_KEY',         '1jl/vqfs&amp;lt;XhdXoAPz9 &lt;span class="highlight"&gt;DO NOT COPY THESE VALUES&lt;/span&gt; c_j{iwqD^&amp;lt;+c9.k&amp;lt;J@4H');
define('SECURE_AUTH_KEY',  'E2N-h2]Dcvp+aS/p7X &lt;span class="highlight"&gt;DO NOT COPY THESE VALUES&lt;/span&gt; {Ka(f;rv?Pxf})CgLi-3');
define('LOGGED_IN_KEY',    'W(50,{W^,OPB%PB&amp;lt;JF &lt;span class="highlight"&gt;DO NOT COPY THESE VALUES&lt;/span&gt; 2;y&amp;amp;,2m%3]R6DUth[;88');
define('NONCE_KEY',        'll,4UC)7ua+8&amp;lt;!4VM+ &lt;span class="highlight"&gt;DO NOT COPY THESE VALUES&lt;/span&gt; #`DXF+[$atzM7 o^-C7g');
define('AUTH_SALT',        'koMrurzOA+|L_lG}kf &lt;span class="highlight"&gt;DO NOT COPY THESE VALUES&lt;/span&gt;  07VC*Lj*lD&amp;amp;?3w!BT#-');
define('SECURE_AUTH_SALT', 'p32*p,]z%LZ+pAu:VY &lt;span class="highlight"&gt;DO NOT COPY THESE VALUES&lt;/span&gt; C-?y+K0DK_+F|0h{!_xY');
define('LOGGED_IN_SALT',   'i^/G2W7!-1H2OQ+t$3 &lt;span class="highlight"&gt;DO NOT COPY THESE VALUES&lt;/span&gt; t6**bRVFSD[Hi])-qS`|');
define('NONCE_SALT',       'Q6]U:K?j4L%Z]}h^q7 &lt;span class="highlight"&gt;DO NOT COPY THESE VALUES&lt;/span&gt; 1% ^qUswWgn+6&amp;amp;xqHN&amp;amp;%');
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These are configuration lines that you will paste directly into your configuration file to set secure keys. Copy the output you received to your clipboard, and then open the WordPress configuration file located in your document root:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /var/www/&lt;span class="highlight"&gt;wordpress&lt;/span&gt;/wp-config.php
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Find the section that contains the dummy values for those settings. It will look something like this:&lt;/p&gt;
&lt;div class="code-label " title="/var/www/wordpress/wp-config.php"&gt;/var/www/wordpress/wp-config.php&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .

define('AUTH_KEY',         'put your unique phrase here');
define('SECURE_AUTH_KEY',  'put your unique phrase here');
define('LOGGED_IN_KEY',    'put your unique phrase here');
define('NONCE_KEY',        'put your unique phrase here');
define('AUTH_SALT',        'put your unique phrase here');
define('SECURE_AUTH_SALT', 'put your unique phrase here');
define('LOGGED_IN_SALT',   'put your unique phrase here');
define('NONCE_SALT',       'put your unique phrase here');

. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Delete these lines and paste in the values you copied from the command line:&lt;/p&gt;
&lt;div class="code-label " title="/var/www/wordpress/wp-config.php"&gt;/var/www/wordpress/wp-config.php&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .

define('AUTH_KEY',         '&lt;span class="highlight"&gt;VALUES COPIED FROM THE COMMAND LINE&lt;/span&gt;');
define('SECURE_AUTH_KEY',  '&lt;span class="highlight"&gt;VALUES COPIED FROM THE COMMAND LINE&lt;/span&gt;');
define('LOGGED_IN_KEY',    '&lt;span class="highlight"&gt;VALUES COPIED FROM THE COMMAND LINE&lt;/span&gt;');
define('NONCE_KEY',        '&lt;span class="highlight"&gt;VALUES COPIED FROM THE COMMAND LINE&lt;/span&gt;');
define('AUTH_SALT',        '&lt;span class="highlight"&gt;VALUES COPIED FROM THE COMMAND LINE&lt;/span&gt;');
define('SECURE_AUTH_SALT', '&lt;span class="highlight"&gt;VALUES COPIED FROM THE COMMAND LINE&lt;/span&gt;');
define('LOGGED_IN_SALT',   '&lt;span class="highlight"&gt;VALUES COPIED FROM THE COMMAND LINE&lt;/span&gt;');
define('NONCE_SALT',       '&lt;span class="highlight"&gt;VALUES COPIED FROM THE COMMAND LINE&lt;/span&gt;');

. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, modify the database connection settings at the top of the file. You need to adjust the database name, the database user, and the associated password that you’ve configured within MariaDB.&lt;/p&gt;

&lt;p&gt;The other change you must make is to set the method that WordPress should use to write to the filesystem. Since we've given the web server permission to write where it needs to, we can explicitly set the filesystem method to "direct". Failure to set this with our current settings would result in WordPress prompting for FTP credentials when you perform certain actions.&lt;/p&gt;

&lt;p&gt;This setting can be added below the database connection settings, or anywhere else in the file:&lt;/p&gt;
&lt;div class="code-label " title="/var/www/wordpress/wp-config.php"&gt;/var/www/wordpress/wp-config.php&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .

define('DB_NAME', '&lt;span class="highlight"&gt;wordpress&lt;/span&gt;');

/** MySQL database username */
define('DB_USER', '&lt;span class="highlight"&gt;wordpressuser&lt;/span&gt;');

/** MySQL database password */
define('DB_PASSWORD', '&lt;span class="highlight"&gt;password&lt;/span&gt;');

. . .

&lt;span class="highlight"&gt;define('FS_METHOD', 'direct');&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save and close the file when you are finished. Finally, you can finish installing and configuring WordPress by accessing it through your web browser.&lt;/p&gt;

&lt;h2 id="step-6-—-completing-the-installation-through-the-web-interface"&gt;Step 6 — Completing the Installation Through the Web Interface&lt;/h2&gt;

&lt;p&gt;Now that the server configuration is complete, we can complete the installation through the web interface.&lt;/p&gt;

&lt;p&gt;In your web browser, navigate to your server's domain name or public IP address:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;https://&lt;span class="highlight"&gt;server_domain_or_IP&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Select the language you would like to use:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/wordpress_lamp_1604/language_selection.png" alt="WordPress language selection"&gt;&lt;/p&gt;

&lt;p&gt;Next, you will come to the main setup page. Select a name for your WordPress site and choose a username (it is recommended not to choose something like "admin" for security purposes). A strong password is generated automatically. Save this password or select an alternative strong password.&lt;/p&gt;

&lt;p&gt;Enter your email address and select whether you want to discourage search engines from indexing your site:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/wordpress_lamp_1604/setup_installation.png" alt="WordPress setup installation"&gt;&lt;/p&gt;

&lt;p&gt;When ready, click the &lt;strong&gt;Install WordPress&lt;/strong&gt; button. You’ll be taken to a page that prompts you to log in:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/wordpress_lamp_1604/login_prompt.png" alt="WordPress login prompt"&gt;&lt;/p&gt;

&lt;p&gt;Once you log in, you will be taken to the WordPress administration dashboard:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/wordpress_lamp_1604/admin_screen.png" alt="WordPress login prompt"&gt;&lt;/p&gt;

&lt;p&gt;From the dashboard, you can begin making changes to your site’s theme and publishing content.&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;WordPress should be installed and ready to use! Some common next steps are to choose the permalinks setting for your posts (which can be found in &lt;strong&gt;Settings&lt;/strong&gt; &amp;gt; &lt;strong&gt;Permalinks&lt;/strong&gt;) or to select a new theme (in &lt;strong&gt;Appearance &amp;gt; Themes&lt;/strong&gt;). If this is your first time using WordPress, explore the interface a bit to get acquainted with your new CMS.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-create-a-self-signed-ssl-certificate-for-apache-in-debian-9</id>
    <published>2018-09-07T14:37:06Z</published>
    <updated>2018-09-07T15:04:32Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-create-a-self-signed-ssl-certificate-for-apache-in-debian-9"/>
    <title>How To Create a Self-Signed SSL Certificate for Apache in Debian 9</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;TLS&lt;/em&gt;, or transport layer security, and its predecessor &lt;em&gt;SSL&lt;/em&gt;, which stands for secure sockets layer, are web protocols used to wrap normal traffic in a protected, encrypted wrapper.&lt;/p&gt;

&lt;p&gt;Using this technology, servers can send traffic safely between servers and clients without the possibility of messages being intercepted by outside parties. The certificate system also assists users in verifying the identity of the sites that they are connecting with.&lt;/p&gt;

&lt;p&gt;In this guide, we will show you how to set up a self-signed SSL certificate for use with an Apache web server on Debian 9.&lt;/p&gt;

&lt;span class='note'&gt;&lt;p&gt;
&lt;strong&gt;Note:&lt;/strong&gt; A self-signed certificate will encrypt communication between your server and any clients. However, because it is not signed by any of the trusted certificate authorities included with web browsers, users cannot use the certificate to validate the identity of your server automatically.&lt;/p&gt;

&lt;p&gt;A self-signed certificate may be appropriate if you do not have a domain name associated with your server and for instances where an encrypted web interface is not user-facing. If you &lt;em&gt;do&lt;/em&gt; have a domain name, in many cases it is better to use a CA-signed certificate. You can find out how to set up a free trusted certificate with the Let's Encrypt project &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-secure-apache-with-let-s-encrypt-on-debian-9"&gt;here&lt;/a&gt;.&lt;br&gt;&lt;/p&gt;&lt;/span&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;Before you begin, you should have a non-root user configured with &lt;code&gt;sudo&lt;/code&gt; privileges. You can learn how to set up such a user account by following our &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-debian-9"&gt;Initial Server Setup with Debian 9&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You will also need to have the Apache web server installed. If you would like to install an entire LAMP (Linux, Apache, MariaDB, PHP) stack on your server, you can follow our guide on &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-linux-apache-mariadb-php-lamp-stack-debian9"&gt;setting up LAMP on Debian 9&lt;/a&gt;. If you just want the Apache web server, skip the steps pertaining to PHP and MariaDB.&lt;/p&gt;

&lt;p&gt;When you have completed these prerequisites, continue below.&lt;/p&gt;

&lt;h2 id="step-1-—-creating-the-ssl-certificate"&gt;Step 1 — Creating the SSL Certificate&lt;/h2&gt;

&lt;p&gt;TLS/SSL works by using a combination of a public certificate and a private key. The SSL key is kept secret on the server. It is used to encrypt content sent to clients. The SSL certificate is publicly shared with anyone requesting the content. It can be used to decrypt the content signed by the associated SSL key.&lt;/p&gt;

&lt;p&gt;We can create a self-signed key and certificate pair with OpenSSL in a single command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/ssl/private/apache-selfsigned.key -out /etc/ssl/certs/apache-selfsigned.crt
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will be asked a series of questions. Before we go over that, let's take a look at what is happening in the command we are issuing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;openssl&lt;/strong&gt;: This is the basic command line tool for creating and managing OpenSSL certificates, keys, and other files.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;req&lt;/strong&gt;: This subcommand specifies that we want to use X.509 certificate signing request (CSR) management. The "X.509" is a public key infrastructure standard that SSL and TLS adheres to for its key and certificate management. We want to create a new X.509 cert, so we are using this subcommand.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;-x509&lt;/strong&gt;: This further modifies the previous subcommand by telling the utility that we want to make a self-signed certificate instead of generating a certificate signing request, as would normally happen.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;-nodes&lt;/strong&gt;: This tells OpenSSL to skip the option to secure our certificate with a passphrase. We need Apache to be able to read the file, without user intervention, when the server starts up. A passphrase would prevent this from happening because we would have to enter it after every restart.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;-days 365&lt;/strong&gt;: This option sets the length of time that the certificate will be considered valid. We set it for one year here.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;-newkey rsa:2048&lt;/strong&gt;: This specifies that we want to generate a new certificate and a new key at the same time. We did not create the key that is required to sign the certificate in a previous step, so we need to create it along with the certificate. The &lt;code&gt;rsa:2048&lt;/code&gt; portion tells it to make an RSA key that is 2048 bits long.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;-keyout&lt;/strong&gt;: This line tells OpenSSL where to place the generated private key file that we are creating.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;-out&lt;/strong&gt;: This tells OpenSSL where to place the certificate that we are creating.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As we stated above, these options will create both a key file and a certificate. We will be asked a few questions about our server in order to embed the information correctly in the certificate.&lt;/p&gt;

&lt;p&gt;Fill out the prompts appropriately. &lt;strong&gt;The most important line is the one that requests the &lt;code&gt;Common Name (e.g. server FQDN or YOUR name)&lt;/code&gt;. You need to enter the domain name associated with your server or, more likely, your server's public IP address.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The entirety of the prompts will look something like this:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Country Name (2 letter code) [AU]:&lt;span class="highlight"&gt;US&lt;/span&gt;
State or Province Name (full name) [Some-State]:&lt;span class="highlight"&gt;New York&lt;/span&gt;
Locality Name (eg, city) []:&lt;span class="highlight"&gt;New York City&lt;/span&gt;
Organization Name (eg, company) [Internet Widgits Pty Ltd]:&lt;span class="highlight"&gt;Bouncy Castles, Inc.&lt;/span&gt;
Organizational Unit Name (eg, section) []:&lt;span class="highlight"&gt;Ministry of Water Slides&lt;/span&gt;
Common Name (e.g. server FQDN or YOUR name) []:&lt;span class="highlight"&gt;server_IP_address&lt;/span&gt;
Email Address []:&lt;span class="highlight"&gt;admin@your_domain.com&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Both of the files you created will be placed in the appropriate subdirectories under &lt;code&gt;/etc/ssl&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id="step-2-—-configuring-apache-to-use-ssl"&gt;Step 2 — Configuring Apache to Use SSL&lt;/h2&gt;

&lt;p&gt;We have created our key and certificate files under the &lt;code&gt;/etc/ssl&lt;/code&gt; directory. Now we just need to modify our Apache configuration to take advantage of these.&lt;/p&gt;

&lt;p&gt;We will make a few adjustments to our configuration:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; We will create a configuration snippet to specify strong default SSL settings.&lt;/li&gt;
&lt;li&gt; We will modify the included SSL Apache Virtual Host file to point to our generated SSL certificates.&lt;/li&gt;
&lt;li&gt; (Recommended) We will modify the unencrypted Virtual Host file to automatically redirect requests to the encrypted Virtual Host.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When we are finished, we should have a secure SSL configuration.&lt;/p&gt;

&lt;h3 id="creating-an-apache-configuration-snippet-with-strong-encryption-settings"&gt;Creating an Apache Configuration Snippet with Strong Encryption Settings&lt;/h3&gt;

&lt;p&gt;First, we will create an Apache configuration snippet to define some SSL settings. This will set Apache up with a strong SSL cipher suite and enable some advanced features that will help keep our server secure. The parameters we will set can be used by any Virtual Hosts enabling SSL.&lt;/p&gt;

&lt;p&gt;Create a new snippet in the &lt;code&gt;/etc/apache2/conf-available&lt;/code&gt; directory. We will name the file &lt;code&gt;ssl-params.conf&lt;/code&gt; to make its purpose clear:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/apache2/conf-available/ssl-params.conf
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To set up Apache SSL securely, we will be using the recommendations by Remy van Elst on the &lt;a href="https://cipherli.st"&gt;Cipherli.st&lt;/a&gt; site. This site is designed to provide easy-to-consume encryption settings for popular software.&lt;/p&gt;

&lt;span class='note'&gt;&lt;p&gt;
The suggested settings on the site linked to above offer strong security. Sometimes, this comes at the cost of greater client compatibility. If you need to support older clients, there is an alternative list that can be accessed by clicking the link on the page labelled "Yes, give me a ciphersuite that works with legacy / old software." That list can be substituted for the items copied below.&lt;/p&gt;

&lt;p&gt;The choice of which config you use will depend largely on what you need to support. They both will provide great security.&lt;br&gt;&lt;/p&gt;&lt;/span&gt;

&lt;p&gt;For our purposes, we can copy the provided settings in their entirety. We will just make one small change to this and disable the &lt;code&gt;Strict-Transport-Security&lt;/code&gt; header (HSTS).&lt;/p&gt;

&lt;p&gt;Preloading HSTS provides increased security, but can have far-reaching consequences if accidentally enabled or enabled incorrectly. In this guide, we will not enable the settings, but you can modify that if you are sure you understand the implications.&lt;/p&gt;

&lt;p&gt;Before deciding, take a moment to read up on &lt;a href="https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security"&gt;HTTP Strict Transport Security, or HSTS&lt;/a&gt;, and specifically about the &lt;a href="https://hstspreload.appspot.com/"&gt;"preload" functionality&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Paste the following configuration into the &lt;code&gt;ssl-params.conf&lt;/code&gt; file we opened:&lt;/p&gt;
&lt;div class="code-label " title="/etc/apache2/conf-available/ssl-params.conf"&gt;/etc/apache2/conf-available/ssl-params.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;SSLCipherSuite EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH
SSLProtocol All -SSLv2 -SSLv3 -TLSv1 -TLSv1.1
SSLHonorCipherOrder On
&lt;span class="highlight"&gt;# Disable preloading HSTS for now.  You can use the commented out header line that includes&lt;/span&gt;
&lt;span class="highlight"&gt;# the "preload" directive if you understand the implications.&lt;/span&gt;
&lt;span class="highlight"&gt;#&lt;/span&gt; Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
Header always set X-Frame-Options DENY
Header always set X-Content-Type-Options nosniff
# Requires Apache &amp;gt;= 2.4
SSLCompression off
SSLUseStapling on
SSLStaplingCache "shmcb:logs/stapling-cache(150000)"
# Requires Apache &amp;gt;= 2.4.11
SSLSessionTickets Off
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save and close the file when you are finished.&lt;/p&gt;

&lt;h3 id="modifying-the-default-apache-ssl-virtual-host-file"&gt;Modifying the Default Apache SSL Virtual Host File&lt;/h3&gt;

&lt;p&gt;Next, let's modify &lt;code&gt;/etc/apache2/sites-available/default-ssl.conf&lt;/code&gt;, the default Apache SSL Virtual Host file. If you are using a different server block file, substitute its name in the commands below.&lt;/p&gt;

&lt;p&gt;Before we go any further, let's back up the original SSL Virtual Host file:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo cp /etc/apache2/sites-available/default-ssl.conf /etc/apache2/sites-available/default-ssl.conf.bak
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, open the SSL Virtual Host file to make adjustments:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/apache2/sites-available/default-ssl.conf
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Inside, with most of the comments removed, the Virtual Host block should look something like this by default:&lt;/p&gt;
&lt;div class="code-label " title="/etc/apache2/sites-available/default-ssl.conf"&gt;/etc/apache2/sites-available/default-ssl.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&amp;lt;IfModule mod_ssl.c&amp;gt;
        &amp;lt;VirtualHost _default_:443&amp;gt;
                ServerAdmin webmaster@localhost

                DocumentRoot /var/www/html

                ErrorLog ${APACHE_LOG_DIR}/error.log
                CustomLog ${APACHE_LOG_DIR}/access.log combined

                SSLEngine on

                SSLCertificateFile      /etc/ssl/certs/ssl-cert-snakeoil.pem
                SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key

                &amp;lt;FilesMatch "\.(cgi|shtml|phtml|php)$"&amp;gt;
                                SSLOptions +StdEnvVars
                &amp;lt;/FilesMatch&amp;gt;
                &amp;lt;Directory /usr/lib/cgi-bin&amp;gt;
                                SSLOptions +StdEnvVars
                &amp;lt;/Directory&amp;gt;

        &amp;lt;/VirtualHost&amp;gt;
&amp;lt;/IfModule&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We will be making some minor adjustments to the file. We will set the normal things we'd want to adjust in a Virtual Host file (ServerAdmin email address, ServerName, etc.), and adjust the SSL directives to point to our certificate and key files. Again, if you’re using a different document root, be sure to update the &lt;code&gt;DocumentRoot&lt;/code&gt; directive.&lt;/p&gt;

&lt;p&gt;After making these changes, your server block should look similar to this:&lt;/p&gt;
&lt;div class="code-label " title="/etc/apache2/sites-available/default-ssl.conf"&gt;/etc/apache2/sites-available/default-ssl.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&amp;lt;IfModule mod_ssl.c&amp;gt;
        &amp;lt;VirtualHost _default_:443&amp;gt;
                ServerAdmin &lt;span class="highlight"&gt;your_email@example.com&lt;/span&gt;
                &lt;span class="highlight"&gt;ServerName server_domain_or_IP&lt;/span&gt;

                DocumentRoot /var/www/html

                ErrorLog ${APACHE_LOG_DIR}/error.log
                CustomLog ${APACHE_LOG_DIR}/access.log combined

                SSLEngine on

                SSLCertificateFile      /etc/ssl/certs/&lt;span class="highlight"&gt;apache-selfsigned.crt&lt;/span&gt;
                SSLCertificateKeyFile /etc/ssl/private/&lt;span class="highlight"&gt;apache-selfsigned.key&lt;/span&gt;

                &amp;lt;FilesMatch "\.(cgi|shtml|phtml|php)$"&amp;gt;
                                SSLOptions +StdEnvVars
                &amp;lt;/FilesMatch&amp;gt;
                &amp;lt;Directory /usr/lib/cgi-bin&amp;gt;
                                SSLOptions +StdEnvVars
                &amp;lt;/Directory&amp;gt;

        &amp;lt;/VirtualHost&amp;gt;
&amp;lt;/IfModule&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save and close the file when you are finished.&lt;/p&gt;

&lt;h3 id="recommended-modifying-the-http-host-file-to-redirect-to-https"&gt;(Recommended) Modifying the HTTP Host File to Redirect to HTTPS&lt;/h3&gt;

&lt;p&gt;As it stands now, the server will provide both unencrypted HTTP and encrypted HTTPS traffic. For better security, it is recommended in most cases to redirect HTTP to HTTPS automatically. If you do not want or need this functionality, you can safely skip this section.&lt;/p&gt;

&lt;p&gt;To adjust the unencrypted Virtual Host file to redirect all traffic to be SSL encrypted, open the &lt;code&gt;/etc/apache2/sites-available/000-default.conf&lt;/code&gt; file:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/apache2/sites-available/000-default.conf
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Inside, within the &lt;code&gt;VirtualHost&lt;/code&gt; configuration blocks, add a &lt;code&gt;Redirect&lt;/code&gt; directive, pointing all traffic to the SSL version of the site:&lt;/p&gt;
&lt;div class="code-label " title="/etc/apache2/sites-available/000-default.conf"&gt;/etc/apache2/sites-available/000-default.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&amp;lt;VirtualHost *:80&amp;gt;
        . . .

        Redirect "/" "https://&lt;span class="highlight"&gt;your_domain_or_IP&lt;/span&gt;/"

        . . .
&amp;lt;/VirtualHost&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save and close the file when you are finished.&lt;/p&gt;

&lt;p&gt;That’s all of the configuration changes you need to make to Apache. Next, we will discuss how to update firewall rules with &lt;code&gt;ufw&lt;/code&gt; to allow encrypted HTTPS traffic to your server.&lt;/p&gt;

&lt;h2 id="step-3-—-adjusting-the-firewall"&gt;Step 3 — Adjusting the Firewall&lt;/h2&gt;

&lt;p&gt;If you have the &lt;code&gt;ufw&lt;/code&gt; firewall enabled, as recommended by the prerequisite guides, you might need to adjust the settings to allow for SSL traffic. Fortunately, when installed on Debian 9, &lt;code&gt;ufw&lt;/code&gt; comes loaded with app profiles which you can use to tweak your firewall settings&lt;/p&gt;

&lt;p&gt;We can see the available profiles by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw app list
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see a list like this, with the following four profiles near the bottom of the output:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Available applications:
. . .
  WWW
  WWW Cache
  WWW Full
  WWW Secure
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can see the current setting by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw status
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you allowed only regular HTTP traffic earlier, your output might look like this:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Status: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere
WWW                        ALLOW       Anywhere
OpenSSH (v6)               ALLOW       Anywhere (v6)
WWW (v6)                   ALLOW       Anywhere (v6)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To additionally let in HTTPS traffic, allow the "WWW Full" profile and then delete the redundant "WWW" profile allowance:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw allow 'WWW Full'
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo ufw delete allow 'WWW'
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Your status should look like this now:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw status
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Status: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere
WWW Full                   ALLOW       Anywhere
OpenSSH (v6)               ALLOW       Anywhere (v6)
WWW Full (v6)              ALLOW       Anywhere (v6)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With your firewall configured to allow HTTPS traffic, you can move on to the next step where we’ll go over how to enable a few modules and configuration files to allow SSL to function properly.&lt;/p&gt;

&lt;h2 id="step-4-—-enabling-the-changes-in-apache"&gt;Step 4 — Enabling the Changes in Apache&lt;/h2&gt;

&lt;p&gt;Now that we've made our changes and adjusted our firewall, we can enable the SSL and headers modules in Apache, enable our SSL-ready Virtual Host, and then restart Apache to put these changes into effect.&lt;/p&gt;

&lt;p&gt;Enable &lt;code&gt;mod_ssl&lt;/code&gt;, the Apache SSL module, and &lt;code&gt;mod_headers&lt;/code&gt;, which is needed by some of the settings in our SSL snippet, with the &lt;code&gt;a2enmod&lt;/code&gt; command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo a2enmod ssl
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo a2enmod headers
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, enable your SSL Virtual Host with the &lt;code&gt;a2ensite&lt;/code&gt; command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo a2ensite default-ssl
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will also need to enable your &lt;code&gt;ssl-params.conf&lt;/code&gt; file, to read in the values you’ve set:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo a2enconf ssl-params
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At this point, the site and the necessary modules are enabled. We should check to make sure that there are no syntax errors in our files. Do this by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apache2ctl configtest
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If everything is successful, you will get a result that looks like this:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Syntax OK
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As long as your output has &lt;code&gt;Syntax OK&lt;/code&gt; in it, then your configuration file has no syntax errors and you can safely restart Apache to implement the changes:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl restart apache2
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With that, your self-signed SSL certificate is all set. You can now test that your server is correctly encrypting its traffic.&lt;/p&gt;

&lt;h2 id="step-5-—-testing-encryption"&gt;Step 5 — Testing Encryption&lt;/h2&gt;

&lt;p&gt;You’re now ready to test your SSL server.&lt;/p&gt;

&lt;p&gt;Open your web browser and type &lt;code&gt;https://&lt;/code&gt; followed by your server's domain name or IP into the address bar:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;https://&lt;span class="highlight"&gt;server_domain_or_IP&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Because the certificate you created isn't signed by one of your browser's trusted certificate authorities, you will likely see a scary looking warning like the one below:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/apache_ssl_1604/self_signed_warning.png" alt="Apache self-signed cert warning"&gt;&lt;/p&gt;

&lt;p&gt;This is expected and normal. We are only interested in the encryption aspect of our certificate, not the third party validation of our host's authenticity. Click &lt;strong&gt;ADVANCED&lt;/strong&gt; and then the link provided to proceed to your host anyways:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/apache_ssl_1604/warning_override.png" alt="Apache self-signed override"&gt;&lt;/p&gt;

&lt;p&gt;You should be taken to your site. If you look in the browser address bar, you will see a lock with an "x" over it or another similar “not secure” notice. In this case, this just means that the certificate cannot be validated. It is still encrypting your connection.&lt;/p&gt;

&lt;p&gt;If you configured Apache to redirect HTTP to HTTPS, you can also check whether the redirect functions correctly:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;http://&lt;span class="highlight"&gt;server_domain_or_IP&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If this results in the same icon, this means that your redirect worked correctly. However, the redirect you created earlier is only a temporary redirect. If you’d like to make the redirection to HTTPS permanent, continue on to the final step.&lt;/p&gt;

&lt;h2 id="step-6-—-changing-to-a-permanent-redirect"&gt;Step 6 — Changing to a Permanent Redirect&lt;/h2&gt;

&lt;p&gt;If your redirect worked correctly and you are sure you want to allow only encrypted traffic, you should modify the unencrypted Apache Virtual Host again to make the redirect permanent.&lt;/p&gt;

&lt;p&gt;Open your server block configuration file again:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/apache2/sites-available/000-default.conf
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Find the &lt;code&gt;Redirect&lt;/code&gt; line we added earlier. Add &lt;code&gt;permanent&lt;/code&gt; to that line, which changes the redirect from a 302 temporary redirect to a 301 permanent redirect:&lt;/p&gt;
&lt;div class="code-label " title="/etc/apache2/sites-available/000-default.conf"&gt;/etc/apache2/sites-available/000-default.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&amp;lt;VirtualHost *:80&amp;gt;
        . . .

        Redirect &lt;span class="highlight"&gt;permanent&lt;/span&gt; "/" "https://&lt;span class="highlight"&gt;your_domain_or_IP&lt;/span&gt;/"

        . . .
&amp;lt;/VirtualHost&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save and close the file.&lt;/p&gt;

&lt;p&gt;Check your configuration for syntax errors:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apache2ctl configtest
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If this command doesn’t report any syntax errors, restart Apache:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl restart apache2
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will make the redirect permanent, and your site will only serve traffic over HTTPS.&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;You have configured your Apache server to use strong encryption for client connections. This will allow you serve requests securely, and will prevent outside parties from reading your traffic.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-install-and-configure-gitlab-on-debian-9</id>
    <published>2018-09-07T15:04:08Z</published>
    <updated>2018-09-07T15:04:23Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-install-and-configure-gitlab-on-debian-9"/>
    <title>How To Install and Configure GitLab on Debian 9</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://gitlab.com/gitlab-org/gitlab-ce"&gt;GitLab CE&lt;/a&gt;, or Community Edition, is an open-source application primarily used to host Git repositories, with additional development-related features like issue tracking.  It is designed to be hosted using your own infrastructure, and provides flexibility in deploying as an internal repository store for your development team, a public way to interface with users, or a means for contributors to host their own projects.&lt;/p&gt;

&lt;p&gt;The GitLab project makes it relatively straightforward to set up a GitLab instance on your own hardware with an easy installation mechanism.  In this guide, we will cover how to install and configure GitLab on a Debian 9 server.&lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;For this tutorial, you will need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Debian 9 server with a non-&lt;strong&gt;root&lt;/strong&gt; &lt;code&gt;sudo&lt;/code&gt; user and basic firewall.  To set this up, follow our &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-debian-9"&gt;Debian 9 initial server setup guide&lt;/a&gt;. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;a href="http://docs.gitlab.com/ce/install/requirements.html#hardware-requirements"&gt;published GitLab hardware requirements&lt;/a&gt; recommend using a server with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;2 cores&lt;/li&gt;
&lt;li&gt;8GB of RAM&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Although you may be able to get by with substituting some swap space for RAM, it is not recommended.  For this guide we will assume that you have the above resources as a minimum.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A domain name pointed at your server.  For more information, see our documentation on how to &lt;a href="https://www.digitalocean.com/docs/networking/dns/quickstart/"&gt;get started with DNS on DigitalOcean&lt;/a&gt;.  This tutorial will use the domain name &lt;strong&gt;example.com&lt;/strong&gt; for demonstration purposes.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="step-1-—-installing-the-dependencies"&gt;Step 1 — Installing the Dependencies&lt;/h2&gt;

&lt;p&gt;Before we can install GitLab itself, it is important to install some of the software that it leverages during installation and on an ongoing basis.  Fortunately, all of the required software can be easily installed from Debian's default package repositories.&lt;/p&gt;

&lt;p&gt;Since this is our first time using &lt;code&gt;apt&lt;/code&gt; during this session, we can refresh the local package index and then install the dependencies by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt update
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo apt install ca-certificates curl openssh-server postfix
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You may have some of this software installed already.  For the &lt;code&gt;postfix&lt;/code&gt; installation, select &lt;strong&gt;Internet Site&lt;/strong&gt; when prompted.  On the next screen, enter your server's domain name to configure how the system will send mail.&lt;/p&gt;

&lt;h2 id="step-2-—-installing-gitlab"&gt;Step 2 — Installing GitLab&lt;/h2&gt;

&lt;p&gt;Now that the dependencies are in place, we can install GitLab itself.  This is a straightforward process that leverages an installation script to configure your system with the GitLab repositories.&lt;/p&gt;

&lt;p&gt;Move into the &lt;code&gt;/tmp&lt;/code&gt; directory and then download the installation script:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cd /tmp
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;curl -LO https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.deb.sh
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Feel free to examine the downloaded script to ensure that you are comfortable with the actions it will take.  You can also find a hosted version of the script &lt;a href="https://packages.gitlab.com/gitlab/gitlab-ce/install"&gt;here&lt;/a&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;less /tmp/script.deb.sh
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once you are satisfied with the safety of the script, run the installer:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo bash /tmp/script.deb.sh
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The script will set up your server to use the GitLab maintained repositories.  This lets you manage GitLab with the same package management tools you use for your other system packages.  Once this is complete, you can install the actual GitLab application with &lt;code&gt;apt&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install gitlab-ce
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will install the necessary components on your system.&lt;/p&gt;

&lt;h2 id="step-3-—-adjusting-the-firewall-rules"&gt;Step 3 — Adjusting the Firewall Rules&lt;/h2&gt;

&lt;p&gt;Before you configure GitLab, you will need to ensure that your firewall rules are permissive enough to allow web traffic.  If you followed the guide linked in the prerequisites, you will have a &lt;code&gt;ufw&lt;/code&gt; firewall enabled.&lt;/p&gt;

&lt;p&gt;View the current status of your active firewall by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw status
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Status: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere                  
OpenSSH (v6)               ALLOW       Anywhere (v6)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, the current rules allow SSH traffic through, but access to other services is restricted.  Since GitLab is a web application, we should allow HTTP access.  Because we will be taking advantage of GitLab's ability to request and enable a free TLS/SSL certificate from &lt;a href="https://letsencrypt.org/"&gt;Let's Encrypt&lt;/a&gt;, let's also allow HTTPS access.&lt;/p&gt;

&lt;p&gt;We can allow access to both HTTP and HTTPS by allowing the "WWW Full" app profile through our firewall. If you didn't already have OpenSSH traffic enabled, you should allow that traffic now too:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw allow "WWW Full"
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo ufw allow OpenSSH
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Check the &lt;code&gt;ufw status&lt;/code&gt; again, this time appending the &lt;code&gt;verbose&lt;/code&gt; flag; you should see access configured to at least these two services:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw status verbose
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
New profiles: skip

To                         Action      From
--                         ------      ----
22/tcp (OpenSSH)           ALLOW IN    Anywhere                  
80,443/tcp (WWW Full)      ALLOW IN    Anywhere                  
22/tcp (OpenSSH (v6))      ALLOW IN    Anywhere (v6)             
80,443/tcp (WWW Full (v6)) ALLOW IN    Anywhere (v6)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The above output indicates that the GitLab web interface will be accessible once we configure the application.&lt;/p&gt;

&lt;h2 id="step-4-—-editing-the-gitlab-configuration-file"&gt;Step 4 — Editing the GitLab Configuration File&lt;/h2&gt;

&lt;p&gt;Before you can use the application, you need to update the configuration file and run a reconfiguration command.  First, open Gitlab's configuration file:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/gitlab/gitlab.rb
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Near the top is the &lt;code&gt;external_url&lt;/code&gt; configuration line.  Update it to match your domain.  Change &lt;code&gt;http&lt;/code&gt; to &lt;code&gt;https&lt;/code&gt; so that GitLab will automatically redirect users to the site protected by the Let's Encrypt certificate:&lt;/p&gt;
&lt;div class="code-label " title="/etc/gitlab/gitlab.rb"&gt;/etc/gitlab/gitlab.rb&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;##! For more details on configuring external_url see:
##! https://docs.gitlab.com/omnibus/settings/configuration.html#configuring-the-external-url-for-gitlab
external_url 'http&lt;span class="highlight"&gt;s&lt;/span&gt;://&lt;span class="highlight"&gt;example.com&lt;/span&gt;'
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, look for the &lt;code&gt;letsencrypt['contact_emails']&lt;/code&gt; setting.  This setting defines a list of email addresses that the Let's Encrypt project can use to contact you if there are problems with your domain.  It's a good idea to uncomment and fill this out so that you will know of any issues:&lt;/p&gt;
&lt;div class="code-label " title="/etc/gitlab/gitlab.rb"&gt;/etc/gitlab/gitlab.rb&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;letsencrypt['contact_emails'] = ['&lt;span class="highlight"&gt;sammy@example.com&lt;/span&gt;']
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save and close the file. Run the following command to reconfigure Gitlab:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo gitlab-ctl reconfigure
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will initialize GitLab using the information it can find about your server.  This is a completely automated process, so you will not have to answer any prompts. The process will also configure a Let's Encrypt certificate for your domain.&lt;/p&gt;

&lt;h2 id="step-5-—-performing-initial-configuration-through-the-web-interface"&gt;Step 5 — Performing Initial Configuration Through the Web Interface&lt;/h2&gt;

&lt;p&gt;With GitLab running and access permitted, we can perform some initial configuration of the application through the web interface.&lt;/p&gt;

&lt;h3 id="logging-in-for-the-first-time"&gt;Logging In for the First Time&lt;/h3&gt;

&lt;p&gt;Visit the domain name of your GitLab server in your web browser:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;https://&lt;span class="highlight"&gt;example.com&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On your first time visiting, you should see an initial prompt to set a password for the administrative account:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/gitlab_install_1604/gitlab_initial_password2.png" alt="GitLab initial password set prompt"&gt;&lt;/p&gt;

&lt;p&gt;In the initial password prompt, supply and confirm a secure password for the administrative account.  Click on the &lt;strong&gt;Change your password&lt;/strong&gt; button when you are finished.&lt;/p&gt;

&lt;p&gt;You will be redirected to the conventional GitLab login page:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/gitlab_install_1604/gitlab_first_signin2.png" alt="GitLab first sign in prompt"&gt;&lt;/p&gt;

&lt;p&gt;Here, you can log in with the password you just set.  The credentials are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Username: &lt;strong&gt;root&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Password: [the password you set]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Enter these values into the fields for existing users and click the &lt;strong&gt;Sign in&lt;/strong&gt; button.  You will be signed into the application and taken to a landing page that prompts you to begin adding projects:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/gitlab_install_1604/landing_page2.png" alt="GitLab initial login landing page"&gt;&lt;/p&gt;

&lt;p&gt;You can now make some simple changes to get GitLab set up the way you'd like.&lt;/p&gt;

&lt;h3 id="adjusting-your-profile-settings"&gt;Adjusting your Profile Settings&lt;/h3&gt;

&lt;p&gt;One of the first things you should do after a fresh installation is get your profile into better shape.  GitLab selects some reasonable defaults, but these are not usually appropriate once you start using the software.&lt;/p&gt;

&lt;p&gt;To make the necessary modifications, click on the user icon in the upper-right hand corner of the interface.  In the drop down menu that appears, select &lt;strong&gt;Settings&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/gitlab_install_1604/profile_settings_button2.png" alt="GitLab profile settings button"&gt;&lt;/p&gt;

&lt;p&gt;You will be taken to the &lt;strong&gt;Profile&lt;/strong&gt; section of your settings:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/gitlab_install_1604/profile_settings2.png" alt="GitLab profile settings page"&gt;&lt;/p&gt;

&lt;p&gt;Adjust the &lt;strong&gt;Name&lt;/strong&gt; and &lt;strong&gt;Email&lt;/strong&gt; address from "Administrator" and "&lt;a href="mailto:admin@example.com"&gt;admin@example.com&lt;/a&gt;" to something more accurate.  The name you select will be displayed to other users, while the email will be used for default avatar detection, notifications, Git actions through the interface, etc.&lt;/p&gt;

&lt;p&gt;Click on the &lt;strong&gt;Update Profile settings&lt;/strong&gt; button at the bottom when you are done:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/gitlab_install_1604/update_profile_settings_button2.png" alt="GitLab update profile settings button"&gt;&lt;/p&gt;

&lt;p&gt;A confirmation email will be sent to the address you provided.  Follow the instructions in the email to confirm your account so that you can begin using it with GitLab.&lt;/p&gt;

&lt;h3 id="changing-your-account-name"&gt;Changing Your Account Name&lt;/h3&gt;

&lt;p&gt;Next, click on the &lt;strong&gt;Account&lt;/strong&gt; item in the left-hand menu bar:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/gitlab_install_1604/account_menu_item2.png" alt="GitLab account menu item"&gt;&lt;/p&gt;

&lt;p&gt;Here, you can find your private API token or configure two-factor authentication.  However, the functionality we are interested in at the moment is the &lt;strong&gt;Change username&lt;/strong&gt; section.&lt;/p&gt;

&lt;p&gt;By default, the first administrative account is given the name &lt;strong&gt;root&lt;/strong&gt;.  Since this is a known account name, it is more secure to change this to a different name.  You will still have administrative privileges; the only thing that will change is the name.  Replace &lt;strong&gt;root&lt;/strong&gt; with your preferred username:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/gitlab_install_1604/change_username2.png" alt="GitLab change username section"&gt;&lt;/p&gt;

&lt;p&gt;Click on the &lt;strong&gt;Update username&lt;/strong&gt; button to make the change:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/gitlab_install_1604/update_username_button2.png" alt="GitLab update username button"&gt;&lt;/p&gt;

&lt;p&gt;Next time you log in to the GitLab, remember to use your new username.&lt;/p&gt;

&lt;h3 id="adding-an-ssh-key-to-your-account"&gt;Adding an SSH Key to your Account&lt;/h3&gt;

&lt;p&gt;In most cases, you will want to use SSH keys with Git to interact with your GitLab projects.  To do this, you need to add your SSH public key to your GitLab account.&lt;/p&gt;

&lt;p&gt;If you already have an SSH key pair created on your &lt;strong&gt;local computer&lt;/strong&gt;, you can usually view the public key by typing:&lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cat ~/.ssh/id_rsa.pub
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see a large chunk of text, like this:&lt;/p&gt;
&lt;pre class="code-pre  local-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDMuyMtMl6aWwqBCvQx7YXvZd7bCFVDsyln3yh5/8Pu23LW88VXfJgsBvhZZ9W0rPBGYyzE/TDzwwITvVQcKrwQrvQlYxTVbqZQDlmsC41HnwDfGFXg+QouZemQ2YgMeHfBzy+w26/gg480nC2PPNd0OG79+e7gFVrTL79JA/MyePBugvYqOAbl30h7M1a7EHP3IV5DQUQg4YUq49v4d3AvM0aia4EUowJs0P/j83nsZt8yiE2JEYR03kDgT/qziPK7LnVFqpFDSPC3MR3b8B354E9Af4C/JHgvglv2tsxOyvKupyZonbyr68CqSorO2rAwY/jWFEiArIaVuDiR9YM5 sammy@mydesktop
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Copy this text and head back to the &lt;strong&gt;Settings&lt;/strong&gt; page in GitLab's web interface.&lt;/p&gt;

&lt;p&gt;If, instead, you get a message that looks like this, you do not yet have an SSH key pair configured on your machine:&lt;/p&gt;
&lt;pre class="code-pre  local-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;cat: /home/sammy/.ssh/id_rsa.pub: No such file or directory
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If this is the case, you can create an SSH key pair by typing:&lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;ssh-keygen
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Accept the defaults and optionally provide a password to secure the key locally:&lt;/p&gt;
&lt;pre class="code-pre  local-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Generating public/private rsa key pair.
Enter file in which to save the key (/home/sammy/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/sammy/.ssh/id_rsa.
Your public key has been saved in /home/sammy/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:I8v5/M5xOicZRZq/XRcSBNxTQV2BZszjlWaIHi5chc0 sammy@gitlab.docsthat.work
The key's randomart image is:
+---[RSA 2048]----+
|          ..%o==B|
|           *.E =.|
|        . ++= B  |
|         ooo.o . |
|      . S .o  . .|
|     . + .. .   o|
|      +   .o.o ..|
|       o .++o .  |
|        oo=+     |
+----[SHA256]-----+
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once you have this, you can display your public key as above by typing:&lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cat ~/.ssh/id_rsa.pub
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre  local-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDMuyMtMl6aWwqBCvQx7YXvZd7bCFVDsyln3yh5/8Pu23LW88VXfJgsBvhZZ9W0rPBGYyzE/TDzwwITvVQcKrwQrvQlYxTVbqZQDlmsC41HnwDfGFXg+QouZemQ2YgMeHfBzy+w26/gg480nC2PPNd0OG79+e7gFVrTL79JA/MyePBugvYqOAbl30h7M1a7EHP3IV5DQUQg4YUq49v4d3AvM0aia4EUowJs0P/j83nsZt8yiE2JEYR03kDgT/qziPK7LnVFqpFDSPC3MR3b8B354E9Af4C/JHgvglv2tsxOyvKupyZonbyr68CqSorO2rAwY/jWFEiArIaVuDiR9YM5 sammy@mydesktop
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Copy the block of text that's displayed and head back to your &lt;strong&gt;Settings&lt;/strong&gt; in GitLab's web interface.&lt;/p&gt;

&lt;p&gt;Click on the &lt;strong&gt;SSH Keys&lt;/strong&gt; item in the left-hand menu:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/gitlab_install_1604/ssh_keys_menu_item2.png" alt="GitLab SSH Keys menu item"&gt;&lt;/p&gt;

&lt;p&gt;In the provided space paste the public key you copied from your local machine.  Give it a descriptive title, and click the &lt;strong&gt;Add key&lt;/strong&gt; button:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/gitlab_install_1604/add_ssh_key2.png" alt="GitLab add SSH Key"&gt;&lt;/p&gt;

&lt;p&gt;You should now be able to manage your GitLab projects and repositories from your local machine without having to provide your GitLab account credentials.&lt;/p&gt;

&lt;h2 id="step-6-—-restricting-or-disabling-public-sign-ups-optional"&gt;Step 6 — Restricting or Disabling Public Sign-ups (Optional)&lt;/h2&gt;

&lt;p&gt;You may have noticed that it is possible for anyone to sign up for an account when you visit your GitLab instance's landing page.  This may be what you want if you are looking to host public project.  However, many times, more restrictive settings are desirable.&lt;/p&gt;

&lt;p&gt;To begin, make your way to the administrative area by clicking on the &lt;strong&gt;wrench icon&lt;/strong&gt; in the main menu bar at the top of the page:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/gitlab_install_1604/admin_area_button2.png" alt="GitLab administrative area button"&gt;&lt;/p&gt;

&lt;p&gt;On the page that follows, you can see an overview of your GitLab instance as a whole.  To adjust the settings, click on the &lt;strong&gt;Settings&lt;/strong&gt; item at the bottom of the left-hand menu:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/gitlab_install_1604/admin_settings_button2.png" alt="GitLab administrative settings button"&gt;&lt;/p&gt;

&lt;p&gt;You will be taken to the global settings for your GitLab instance.  Here, you can adjust a number of settings that affect whether new users can sign up and their level of access.&lt;/p&gt;

&lt;h3 id="disabling-sign-ups"&gt;Disabling Sign-ups&lt;/h3&gt;

&lt;p&gt;If you wish to disable sign-ups completely (you can still manually create accounts for new users), scroll down to the &lt;strong&gt;Sign-up Restrictions&lt;/strong&gt; section.&lt;/p&gt;

&lt;p&gt;Deselect the &lt;strong&gt;Sign-up enabled&lt;/strong&gt; check box:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/gitlab_install_1604/deselect_sign-ups_enabled.png" alt="GitLab deselect sign-ups enabled"&gt;&lt;/p&gt;

&lt;p&gt;Scroll down to the bottom and click on the &lt;strong&gt;Save changes&lt;/strong&gt; button: &lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/gitlab_install_1604/new_save_changes_button.png" alt="GitLab save settings button"&gt;&lt;/p&gt;

&lt;p&gt;The sign-up section should now be removed from the GitLab landing page.&lt;/p&gt;

&lt;h3 id="restricting-sign-ups-by-domain"&gt;Restricting Sign-ups By Domain&lt;/h3&gt;

&lt;p&gt;If you are using GitLab as part of an organization that provides email addresses associated with a domain, you can restrict sign-ups by domain instead of completely disabling them.&lt;/p&gt;

&lt;p&gt;In the &lt;strong&gt;Sign-up Restrictions&lt;/strong&gt; section, select the &lt;strong&gt;Send confirmation email on sign-up&lt;/strong&gt; box, which will allow users to log in only after they've confirmed their email.&lt;/p&gt;

&lt;p&gt;Next, add your domain or domains to the &lt;strong&gt;Whitelisted domains for sign-ups&lt;/strong&gt; box, one domain per line.  You can use the asterisk "*" to specify wildcard domains:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/gitlab_install_1604/restrict_sign-ups_by_domain.png" alt="GitLab restrict sign-ups by domain"&gt;&lt;/p&gt;

&lt;p&gt;Scroll down to the bottom and click on the &lt;strong&gt;Save changes&lt;/strong&gt; button: &lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/gitlab_install_1604/new_save_changes_button.png" alt="GitLab save settings button"&gt;&lt;/p&gt;

&lt;p&gt;The sign-up section should now be removed from the GitLab landing page.&lt;/p&gt;

&lt;h3 id="restricting-project-creation"&gt;Restricting Project Creation&lt;/h3&gt;

&lt;p&gt;By default, new users can create up to 10 projects.  If you wish to allow new users from the outside for visibility and participation, but want to restrict their access to creating new projects, you can do so in the &lt;strong&gt;Account and Limit Settings&lt;/strong&gt; section.&lt;/p&gt;

&lt;p&gt;Inside, you can change the &lt;strong&gt;Default projects limit&lt;/strong&gt; to 0 to completely disable new users from creating projects:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/gitlab_install_1604/set_projects_to_zero.png" alt="GitLab set projects to zero"&gt;&lt;/p&gt;

&lt;p&gt;New users can still be added to projects manually and will have access to internal or public projects created by other users.&lt;/p&gt;

&lt;p&gt;Scroll down to the bottom and click on the &lt;strong&gt;Save changes&lt;/strong&gt; button: &lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/gitlab_install_1604/new_save_changes_button.png" alt="GitLab save settings button"&gt;&lt;/p&gt;

&lt;p&gt;New users will now be able to create accounts, but unable to create projects.&lt;/p&gt;

&lt;h3 id="renewing-let-39-s-encrypt-certificates"&gt;Renewing Let's Encrypt Certificates&lt;/h3&gt;

&lt;p&gt;By default, GitLab has a scheduled task set up to renew Let's Encrypt certificates after midnight every fourth day, with the exact minute based on your &lt;code&gt;external_url&lt;/code&gt;.  You can modify these settings in the &lt;code&gt;/etc/gitlab/gitlab.rb&lt;/code&gt; file. For example, if you wanted to renew every 7th day at 12:30, you could configure this as follows:&lt;/p&gt;
&lt;div class="code-label " title="/etc/gitlab/gitlab.rb"&gt;/etc/gitlab/gitlab.rb&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;letsencrypt['auto_renew_hour'] = "12"
letsencrypt['auto_renew_minute'] = "30"
letsencrypt['auto_renew_day_of_month'] = "*/7"
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can also disable auto-renewal by adding an additional setting to &lt;code&gt;/etc/gitlab/gitlab.rb&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-label " title="/etc/gitlab/gitlab.rb"&gt;/etc/gitlab/gitlab.rb&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;letsencrypt['auto_renew'] = false
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With auto-renewals in place, you will not need to worry about service interruptions. &lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;You should now have a working GitLab instance hosted on your own server.  You can begin to import or create new projects and configure the appropriate level of access for your team.  GitLab is regularly adding features and making updates to their platform, so be sure to check out the project's home page to stay up-to-date on any improvements or important notices.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-set-up-vsftpd-for-a-user-s-directory-on-debian-9</id>
    <published>2018-09-07T13:48:13Z</published>
    <updated>2018-09-07T14:44:35Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-set-up-vsftpd-for-a-user-s-directory-on-debian-9"/>
    <title>How To Set Up vsftpd for a User's Directory on Debian 9</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;FTP, short for File Transfer Protocol, is a network protocol that was once widely used for moving files between a client and server. It has since been replaced by faster, more secure, and more convenient ways of delivering files. Many casual internet users expect to download directly from their web browser with &lt;code&gt;https&lt;/code&gt;, and command-line users are more likely to use secure protocols such as the &lt;code&gt;scp&lt;/code&gt; or &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-use-sftp-to-securely-transfer-files-with-a-remote-server"&gt;SFTP&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;FTP is still used to support legacy applications and workflows with very specific needs. If you have a choice of what protocol to use, consider exploring the more modern options. When you do need FTP, however, vsftpd is an excellent choice.  Optimized for security, performance, and stability, vsftpd offers strong protection against many security problems found in other FTP servers and is the default for many Linux distributions.&lt;/p&gt;

&lt;p&gt;In this tutorial, you'll configure vsftpd to allow a user to upload files to his or her home directory using FTP with login credentials secured by SSL/TLS.&lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;To follow along with this tutorial you will need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Debian 9 server, and a non-root user with sudo privileges.  You can learn more about how to create a user with these privileges in our &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-debian-9"&gt;Initial Server Setup with Debian 9&lt;/a&gt; guide.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="step-1-—-installing-vsftpd"&gt;Step 1 — Installing vsftpd&lt;/h2&gt;

&lt;p&gt;Let's start by updating our package list and installing the &lt;code&gt;vsftpd&lt;/code&gt; daemon:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt update
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo apt install vsftpd
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When the installation is complete, let's copy the configuration file so we can start with a blank configuration, and save the original as a backup:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo cp /etc/vsftpd.conf /etc/vsftpd.conf.orig
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With a backup of the configuration in place, we're ready to configure the firewall.&lt;/p&gt;

&lt;h2 id="step-2-—-opening-the-firewall"&gt;Step 2 — Opening the Firewall&lt;/h2&gt;

&lt;p&gt;Let's check the firewall status to see if it’s enabled. If it is, we’ll ensure that FTP traffic is permitted so firewall rules don't block our tests. This guide assumes that you have UFW installed, following Step 4 in the &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-debian-9#step-four-%E2%80%94-setting-up-a-basic-firewall"&gt;initial server setup guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Check the firewall status:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw status
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this case, only SSH is allowed through:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Status: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere
OpenSSH (v6)               ALLOW       Anywhere (v6)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You may have other rules in place or no firewall rules at all. Since only SSH traffic is permitted in this case,  we’ll need to add rules for FTP traffic.&lt;/p&gt;

&lt;p&gt;Let's open ports &lt;code&gt;20&lt;/code&gt; and &lt;code&gt;21&lt;/code&gt; for FTP, port &lt;code&gt;990&lt;/code&gt; for when we enable TLS, and ports &lt;code&gt;40000-50000&lt;/code&gt; for the range of passive ports we plan to set in the configuration file:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw allow 20/tcp
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo ufw allow 21/tcp
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo ufw allow 990/tcp
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo ufw allow 40000:50000/tcp
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Check the firewall status:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw status
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Your firewall rules should now look like this:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Status: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere
990/tcp                    ALLOW       Anywhere
20/tcp                     ALLOW       Anywhere
21/tcp                     ALLOW       Anywhere
40000:50000/tcp            ALLOW       Anywhere
OpenSSH (v6)               ALLOW       Anywhere (v6)
20/tcp (v6)                ALLOW       Anywhere (v6)
21/tcp (v6)                ALLOW       Anywhere (v6)
990/tcp (v6)               ALLOW       Anywhere (v6)
40000:50000/tcp (v6)       ALLOW       Anywhere (v6)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With &lt;code&gt;vsftpd&lt;/code&gt; installed and the necessary ports open, let's move on to creating a dedicated FTP user.&lt;/p&gt;

&lt;h2 id="step-3-—-preparing-the-user-directory"&gt;Step 3 — Preparing the User Directory&lt;/h2&gt;

&lt;p&gt;We will create a dedicated FTP user, but you may already have a user in need of FTP access. We'll take care to preserve an existing user’s access to their data in the instructions that follow. Even so, we recommend that you start with a new user until you've configured and tested your setup.&lt;/p&gt;

&lt;p&gt;First, add a test user:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo adduser &lt;span class="highlight"&gt;sammy&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Assign a password when prompted. Feel free to press &lt;code&gt;ENTER&lt;/code&gt; through the other prompts.&lt;/p&gt;

&lt;p&gt;FTP is generally more secure when users are restricted to a specific directory. &lt;code&gt;vsftpd&lt;/code&gt; accomplishes this with &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-configure-chroot-environments-for-testing-on-an-ubuntu-12-04-vps#what-is-a-chroot-environment"&gt;&lt;code&gt;chroot&lt;/code&gt;&lt;/a&gt; jails. When &lt;code&gt;chroot&lt;/code&gt; is enabled for local users, they are restricted to their home directory by default. However, because of the way &lt;code&gt;vsftpd&lt;/code&gt; secures the directory, it must not be writable by the user. This is fine for a new user who should only connect via FTP, but an existing user may need to write to their home folder if they also have shell access.&lt;/p&gt;

&lt;p&gt;In this example, rather than removing write privileges from the home directory, let's create an &lt;code&gt;ftp&lt;/code&gt; directory to serve as the &lt;code&gt;chroot&lt;/code&gt; and a writable &lt;code&gt;files&lt;/code&gt; directory to hold the actual files.&lt;/p&gt;

&lt;p&gt;Create the &lt;code&gt;ftp&lt;/code&gt; folder:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo mkdir /home/&lt;span class="highlight"&gt;sammy&lt;/span&gt;/ftp
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Set its ownership:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo chown nobody:nogroup /home/&lt;span class="highlight"&gt;sammy&lt;/span&gt;/ftp
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Remove write permissions:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo chmod a-w /home/&lt;span class="highlight"&gt;sammy&lt;/span&gt;/ftp
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Verify the permissions:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ls -la /home/sammy/ftp
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;total 8
4 dr-xr-xr-x  2 nobody nogroup 4096 Aug 24 21:29 .
4 drwxr-xr-x  3 sammy  sammy   4096 Aug 24 21:29 ..
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, let's create the directory for file uploads and assign ownership to the user:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo mkdir /home/&lt;span class="highlight"&gt;sammy&lt;/span&gt;/ftp/files
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo chown &lt;span class="highlight"&gt;sammy&lt;/span&gt;:&lt;span class="highlight"&gt;sammy&lt;/span&gt; /home/&lt;span class="highlight"&gt;sammy&lt;/span&gt;/ftp/files
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A permissions check on the &lt;code&gt;ftp&lt;/code&gt; directory should return the following:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ls -la /home/sammy/ftp
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;total 12
dr-xr-xr-x 3 nobody nogroup 4096 Aug 26 14:01 .
drwxr-xr-x 3 sammy  sammy   4096 Aug 26 13:59 ..
drwxr-xr-x 2 sammy  sammy   4096 Aug 26 14:01 files
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, let's add a &lt;code&gt;test.txt&lt;/code&gt; file to use when we test:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;echo "vsftpd test file" | sudo tee /home/&lt;span class="highlight"&gt;sammy&lt;/span&gt;/ftp/files/test.txt
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that we've secured the &lt;code&gt;ftp&lt;/code&gt; directory and allowed the user access to the &lt;code&gt;files&lt;/code&gt; directory, let's modify our configuration.&lt;/p&gt;

&lt;h2 id="step-4-—-configuring-ftp-access"&gt;Step 4 — Configuring FTP Access&lt;/h2&gt;

&lt;p&gt;We're planning to allow a single user with a local shell account to connect with FTP. The two key settings for this are already set in &lt;code&gt;vsftpd.conf&lt;/code&gt;. Start by opening the config file to verify that the settings in your configuration match those below:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/vsftpd.conf
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class="code-label " title="/etc/vsftpd.conf"&gt;/etc/vsftpd.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .
# Allow anonymous FTP? (Disabled by default).
anonymous_enable=NO
#
# Uncomment this to allow local users to log in.
local_enable=YES
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, let's enable the user to upload files by uncommenting the &lt;code&gt;write_enable&lt;/code&gt; setting:&lt;/p&gt;
&lt;div class="code-label " title="/etc/vsftpd.conf"&gt;/etc/vsftpd.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .
write_enable=&lt;span class="highlight"&gt;YES&lt;/span&gt;
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We’ll also uncomment the &lt;code&gt;chroot&lt;/code&gt; to prevent the FTP-connected user from accessing any files or commands outside the directory tree:&lt;/p&gt;
&lt;div class="code-label " title="/etc/vsftpd.conf"&gt;/etc/vsftpd.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .
chroot_local_user=&lt;span class="highlight"&gt;YES&lt;/span&gt;
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let's also add a &lt;code&gt;user_sub_token&lt;/code&gt; to insert the username in our &lt;code&gt;local_root directory&lt;/code&gt; path so our configuration will work for this user and any additional future users. Add these settings anywhere in the file:&lt;/p&gt;
&lt;div class="code-label " title="/etc/vsftpd.conf"&gt;/etc/vsftpd.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .
&lt;span class="highlight"&gt;user_sub_token=$USER&lt;/span&gt;
&lt;span class="highlight"&gt;local_root=/home/$USER/ftp&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let's also limit the range of ports that can be used for passive FTP to make sure enough connections are available:&lt;/p&gt;
&lt;div class="code-label " title="/etc/vsftpd.conf"&gt;/etc/vsftpd.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .
&lt;span class="highlight"&gt;pasv_min_port=40000&lt;/span&gt;
&lt;span class="highlight"&gt;pasv_max_port=50000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span class='note'&gt;&lt;strong&gt;Note:&lt;/strong&gt; In Step 2, we opened the ports that we set here for the passive port range. If you change the values, be sure to update your firewall settings.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;To allow FTP access on a case-by-case basis, let's set the configuration so that users only have access when they are explicitly added to a list, rather than by default:&lt;/p&gt;
&lt;div class="code-label " title="/etc/vsftpd.conf"&gt;/etc/vsftpd.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .
&lt;span class="highlight"&gt;userlist_enable=YES&lt;/span&gt;
&lt;span class="highlight"&gt;userlist_file=/etc/vsftpd.userlist&lt;/span&gt;
&lt;span class="highlight"&gt;userlist_deny=NO&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;userlist_deny&lt;/code&gt; toggles the logic: When it is set to &lt;code&gt;YES&lt;/code&gt;, users on the list are denied FTP access. When it is set to &lt;code&gt;NO&lt;/code&gt;, only users on the list are allowed access. &lt;/p&gt;

&lt;p&gt;When you're done making the changes, save the file and exit the editor.&lt;/p&gt;

&lt;p&gt;Finally, let's add our user to &lt;code&gt;/etc/vsftpd.userlist&lt;/code&gt;. Use the &lt;code&gt;-a&lt;/code&gt; flag to append to the file:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;echo "&lt;span class="highlight"&gt;sammy&lt;/span&gt;" | sudo tee -a /etc/vsftpd.userlist
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Check that it was added as you expected:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cat /etc/vsftpd.userlist
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;sammy
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Restart the daemon to load the configuration changes:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl restart vsftpd
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With the configuration in place, let's move on to testing FTP access.&lt;/p&gt;

&lt;h2 id="step-5-—-testing-ftp-access"&gt;Step 5 — Testing FTP Access&lt;/h2&gt;

&lt;p&gt;We've configured the server to allow only the user &lt;strong&gt;sammy&lt;/strong&gt; to connect via FTP. Let's make sure that this works as expected.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Anonymous users should fail to connect&lt;/strong&gt;:  We've disabled anonymous access. Let's test that by trying to connect anonymously. If our configuration is set up properly, anonymous users should be denied permission. Open another terminal and run the following command. Be sure to replace &lt;code&gt;&lt;span class="highlight"&gt;203.0.113.0&lt;/span&gt;&lt;/code&gt; with your server's public IP address:&lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;ftp -p &lt;span class="highlight"&gt;203.0.113.0&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre  local-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Connected to 203.0.113.0.
220 (vsFTPd 3.0.3)
Name (203.0.113.0:default): &lt;span class="highlight"&gt;anonymous&lt;/span&gt;
530 Permission denied.
ftp: Login failed.
ftp&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Close the connection:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="ftp&amp;gt;"&gt;bye
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Users other than sammy should fail to connect&lt;/strong&gt;: Next, let's try connecting as our sudo user. They should also be denied access, and it should happen before they're allowed to enter their password:&lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;ftp -p &lt;span class="highlight"&gt;203.0.113.0&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre  local-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Connected to 203.0.113.0.
220 (vsFTPd 3.0.3)
Name (203.0.113.0:default): &lt;span class="highlight"&gt;your_sudo_user&lt;/span&gt;
530 Permission denied.
ftp: Login failed.
ftp&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Close the connection:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="ftp&amp;gt;"&gt;bye
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;The user sammy should be able to connect, read, and write files&lt;/strong&gt;: Let's make sure that our designated user can connect:&lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;ftp -p &lt;span class="highlight"&gt;203.0.113.0&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre  local-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Connected to 203.0.113.0.
220 (vsFTPd 3.0.3)
Name (203.0.113.0:default): &lt;span class="highlight"&gt;sammy&lt;/span&gt;
331 Please specify the password.
Password: &lt;span class="highlight"&gt;your_user's_password&lt;/span&gt;
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let's change into the &lt;code&gt;files&lt;/code&gt; directory and use the &lt;code&gt;get&lt;/code&gt; command to transfer the test file we created earlier to our local machine:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="ftp&amp;gt;"&gt;cd files
&lt;/li&gt;&lt;li class="line" prefix="ftp&amp;gt;"&gt;get test.txt
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre  local-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;229 Entering Extended Passive Mode (|||47398|)
150 Opening BINARY mode data connection for test.txt (17 bytes).
100% |**********************************|    17      146.91 KiB/s    00:00 ETA
226 Transfer complete.
17 bytes received in 00:00 (0.17 KiB/s)
ftp&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, let's upload the file with a new name to test write permissions:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="ftp&amp;gt;"&gt;put test.txt upload.txt
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre  local-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;229 Entering Extended Passive Mode (|||46598|)
150 Ok to send data.
100% |**********************************|    17        8.93 KiB/s    00:00 ETA
226 Transfer complete.
17 bytes sent in 00:00 (0.08 KiB/s)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Close the connection:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="ftp&amp;gt;"&gt;bye
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that we've tested our configuration, let's take steps to further secure our server.&lt;/p&gt;

&lt;h2 id="step-6-—-securing-transactions"&gt;Step 6 — Securing Transactions&lt;/h2&gt;

&lt;p&gt;Since FTP does &lt;em&gt;not&lt;/em&gt; encrypt any data in transit, including user credentials, we'll enable TLS/SSL to provide that encryption.  The first step is to create the SSL certificates for use with &lt;code&gt;vsftpd&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Let's use &lt;code&gt;openssl&lt;/code&gt; to create a new certificate and use the &lt;code&gt;-days&lt;/code&gt; flag to make it valid for one year. In the same command, we'll add a private 2048-bit RSA key.  By setting both the &lt;code&gt;-keyout&lt;/code&gt; and &lt;code&gt;-out&lt;/code&gt; flags to the same value, the private key and the certificate will be located in the same file:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/ssl/private/vsftpd.pem -out /etc/ssl/private/vsftpd.pem
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You'll be prompted to provide address information for your certificate. Substitute your own information for the highlighted values below:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Generating a 2048 bit RSA private key
............................................................................+++
...........+++
writing new private key to '/etc/ssl/private/vsftpd.pem'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:&lt;span class="highlight"&gt;US&lt;/span&gt;
State or Province Name (full name) [Some-State]:&lt;span class="highlight"&gt;NY&lt;/span&gt;
Locality Name (eg, city) []:&lt;span class="highlight"&gt;New York City&lt;/span&gt;
Organization Name (eg, company) [Internet Widgits Pty Ltd]:&lt;span class="highlight"&gt;DigitalOcean&lt;/span&gt;
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []: &lt;span class="highlight"&gt;your_server_ip&lt;/span&gt;
Email Address []:
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For more detailed information about the certificate flags, see &lt;a href="https://www.digitalocean.com/community/tutorials/openssl-essentials-working-with-ssl-certificates-private-keys-and-csrs"&gt;OpenSSL Essentials: Working with SSL Certificates, Private Keys and CSRs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you've created the certificates, open the &lt;code&gt;vsftpd&lt;/code&gt; configuration file again:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/vsftpd.conf
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Toward the bottom of the file, you will see two lines that begin with &lt;code&gt;rsa_&lt;/code&gt;. Comment them out so they look like this:&lt;/p&gt;
&lt;div class="code-label " title="/etc/vsftpd.conf"&gt;/etc/vsftpd.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .
&lt;span class="highlight"&gt;#&lt;/span&gt; rsa_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem
&lt;span class="highlight"&gt;#&lt;/span&gt; rsa_private_key_file=/etc/ssl/private/ssl-cert-snakeoil.key
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Below them, add the following lines that point to the certificate and private key we just created:&lt;/p&gt;
&lt;div class="code-label " title="/etc/vsftpd.conf"&gt;/etc/vsftpd.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .
&lt;span class="highlight"&gt;rsa_cert_file=/etc/ssl/private/vsftpd.pem&lt;/span&gt;
&lt;span class="highlight"&gt;rsa_private_key_file=/etc/ssl/private/vsftpd.pem&lt;/span&gt;
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After that, we will force the use of SSL, which will prevent clients that can't deal with TLS from connecting. This is necessary to ensure that all traffic is encrypted, but it may force your FTP user to change clients. Change &lt;code&gt;ssl_enable&lt;/code&gt; to &lt;code&gt;YES&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-label " title="/etc/vsftpd.conf"&gt;/etc/vsftpd.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .
ssl_enable=&lt;span class="highlight"&gt;YES&lt;/span&gt;
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After that, add the following lines to explicitly deny anonymous connections over SSL and to require SSL for both data transfer and logins:&lt;/p&gt;
&lt;div class="code-label " title="/etc/vsftpd.conf"&gt;/etc/vsftpd.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .
&lt;span class="highlight"&gt;allow_anon_ssl=NO&lt;/span&gt;
&lt;span class="highlight"&gt;force_local_data_ssl=YES&lt;/span&gt;
&lt;span class="highlight"&gt;force_local_logins_ssl=YES&lt;/span&gt;
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After this, configure the server to use TLS, the preferred successor to SSL, by adding the following lines:&lt;/p&gt;
&lt;div class="code-label " title="/etc/vsftpd.conf"&gt;/etc/vsftpd.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .
&lt;span class="highlight"&gt;ssl_tlsv1=YES&lt;/span&gt;
&lt;span class="highlight"&gt;ssl_sslv2=NO&lt;/span&gt;
&lt;span class="highlight"&gt;ssl_sslv3=NO&lt;/span&gt;
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, we will add two more options. First, we will not require SSL reuse because it can break many FTP clients. We will require "high" encryption cipher suites, which currently means key lengths equal to or greater than 128 bits:&lt;/p&gt;
&lt;div class="code-label " title="/etc/vsftpd.conf"&gt;/etc/vsftpd.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .
&lt;span class="highlight"&gt;require_ssl_reuse=NO&lt;/span&gt;
&lt;span class="highlight"&gt;ssl_ciphers=HIGH&lt;/span&gt;
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The finished file section should look like this:&lt;/p&gt;
&lt;div class="code-label " title="/etc/vsftpd.conf"&gt;/etc/vsftpd.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;# This option specifies the location of the RSA certificate to use for SSL
# encrypted connections.
#rsa_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem
#rsa_private_key_file=/etc/ssl/private/ssl-cert-snakeoil.key
rsa_cert_file=/etc/ssl/private/vsftpd.pem
rsa_private_key_file=/etc/ssl/private/vsftpd.pem
ssl_enable=YES
allow_anon_ssl=NO
force_local_data_ssl=YES
force_local_logins_ssl=YES
ssl_tlsv1=YES
ssl_sslv2=NO
ssl_sslv3=NO
require_ssl_reuse=NO
ssl_ciphers=HIGH
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When you're done, save and close the file.&lt;/p&gt;

&lt;p&gt;Restart the server for the changes to take effect:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl restart vsftpd
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At this point, we will no longer be able to connect with an insecure command-line client. If we tried, we'd see something like:&lt;/p&gt;
&lt;pre class="code-pre  local-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;ftp -p &lt;span class="highlight"&gt;203.0.113.0&lt;/span&gt;
Connected to 203.0.113.0.
220 (vsFTPd 3.0.3)
Name (203.0.113.0:default): &lt;span class="highlight"&gt;sammy&lt;/span&gt;
530 Non-anonymous sessions must use encryption.
ftp: Login failed.
ftp&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, let's verify that we can connect using a client that supports TLS.&lt;/p&gt;

&lt;h2 id="step-7-—-testing-tls-with-filezilla"&gt;Step 7 — Testing TLS with FileZilla&lt;/h2&gt;

&lt;p&gt;Most modern FTP clients can be configured to use TLS encryption.  We will demonstrate how to connect with &lt;a href="https://filezilla-project.org/"&gt;FileZilla&lt;/a&gt; because of its cross-platform support. Consult the documentation for other clients.&lt;/p&gt;

&lt;p&gt;When you first open FileZilla, find the Site Manager icon just above the word &lt;strong&gt;Host&lt;/strong&gt;, the left-most icon on the top row.  Click it:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/vsftpd_18_04/vsftpd_images/filezilla_site_manager_vsftpd_18_04.png" alt="Site Manager Screent Shot"&gt;&lt;/p&gt;

&lt;p&gt;A new window will open. Click the &lt;strong&gt;New Site&lt;/strong&gt; button in the bottom right corner:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/vsftp-user/new-site.png" alt="New Site Button"&gt;&lt;br&gt;
Under &lt;strong&gt;My Sites&lt;/strong&gt; a new icon with the words &lt;strong&gt;New Site&lt;/strong&gt; will appear. You can name it now or return later and use the &lt;strong&gt;Rename&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;Fill out the &lt;strong&gt;Host&lt;/strong&gt; field with the name or IP address.  Under the &lt;strong&gt;Encryption&lt;/strong&gt; drop down menu, select &lt;strong&gt;Require explicit FTP over TLS&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;For &lt;strong&gt;Logon Type&lt;/strong&gt;, select &lt;strong&gt;Ask for password&lt;/strong&gt;.  Fill in your FTP user in the &lt;strong&gt;User&lt;/strong&gt; field:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/vsftp-user/site-config2.png" alt="General Settings Tab"&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;Connect&lt;/strong&gt; at the bottom of the interface.  You will be asked for the user's password:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/vsftp-user/user-pass.png" alt="Password Dialogue"&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;OK&lt;/strong&gt; to connect. You should now be connected with your server with TLS/SSL encryption.&lt;/p&gt;

&lt;p&gt;Upon success, you will be presented with a server certificate that looks like this:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/vsftpd_18_04/vsftpd_images/filezilla_certificate_vsftpd.png" alt="Site Certificate Dialogue"&gt;&lt;/p&gt;

&lt;p&gt;When you’ve accepted the certificate, double-click the &lt;code&gt;files&lt;/code&gt; folder and drag &lt;code&gt;upload.txt&lt;/code&gt; to the left to confirm that you’re able to download files:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/vsftpd_18_04/vsftpd_images/filezilla_file_test_vsftpd_18_04.png" alt="Download test.txt"&gt;&lt;/p&gt;

&lt;p&gt;When you’ve done that, right-click on the local copy, rename it to &lt;code&gt;upload-tls.txt&lt;/code&gt; and drag it back to the server to confirm that you can upload files:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/vsftpd_18_04/vsftpd_images/filezilla_file_upload_vsftpd_18_04.png" alt="Rename and Upload"&gt;&lt;/p&gt;

&lt;p&gt;You’ve now confirmed that you can securely and successfully transfer files with SSL/TLS enabled.&lt;/p&gt;

&lt;h2 id="step-8-—-disabling-shell-access-optional"&gt;Step 8 — Disabling Shell Access (Optional)&lt;/h2&gt;

&lt;p&gt;If you're unable to use TLS because of client requirements, you can gain some security by disabling the FTP user's ability to log in any other way. One relatively straightforward way to prevent it is by creating a custom shell. This will not provide any encryption, but it will limit the access of a compromised account to files accessible by FTP.&lt;/p&gt;

&lt;p&gt;First, open a file called &lt;code&gt;ftponly&lt;/code&gt; in the &lt;code&gt;bin&lt;/code&gt; directory:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /bin/ftponly
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add a message telling the user why they are unable to log in:&lt;/p&gt;
&lt;div class="code-label " title="/bin/ftponly"&gt;/bin/ftponly&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;#!/bin/sh
echo "This account is limited to FTP access only."
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save the file and exit your editor.&lt;/p&gt;

&lt;p&gt;Change the permissions to make the file executable:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo chmod a+x /bin/ftponly
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Open the list of valid shells:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/shells
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At the bottom add:&lt;/p&gt;
&lt;div class="code-label " title="/etc/shells"&gt;/etc/shells&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .
/bin/ftponly
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Update the user's shell with the following command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo usermod &lt;span class="highlight"&gt;sammy&lt;/span&gt; -s /bin/ftponly
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now try logging into your server as &lt;strong&gt;sammy&lt;/strong&gt;:&lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;ssh sammy@&lt;span class="highlight"&gt;your_server_ip&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see something like:&lt;/p&gt;
&lt;pre class="code-pre  local-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;This account is limited to FTP access only.
Connection to 203.0.113.0 closed.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This confirms that the user can no longer &lt;code&gt;ssh&lt;/code&gt; to the server and is limited to FTP access only.&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;In this tutorial we covered setting up FTP for users with a local account. If you need to use an external authentication source, you might want to look into &lt;code&gt;vsftpd&lt;/code&gt;'s support of virtual users. This offers a rich set of options through the use of PAM, the Pluggable Authentication Modules, and is a good choice if you manage users in another system such as LDAP or Kerberos.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-install-java-with-apt-on-debian-9</id>
    <published>2018-09-07T04:44:34Z</published>
    <updated>2018-09-07T16:46:45Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-install-java-with-apt-on-debian-9"/>
    <title>How To Install Java with Apt on Debian 9</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;Java and the JVM (Java's virtual machine) are required for many kinds of software, including &lt;a href="http://tomcat.apache.org/"&gt;Tomcat&lt;/a&gt;, &lt;a href="https://www.eclipse.org/jetty/"&gt;Jetty&lt;/a&gt;, &lt;a href="https://javaee.github.io/glassfish/"&gt;Glassfish&lt;/a&gt;, &lt;a href="http://cassandra.apache.org/"&gt;Cassandra&lt;/a&gt; and &lt;a href="https://jenkins.io/"&gt;Jenkins&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this guide, you will install various versions of the Java Runtime Environment (JRE) and the Java Developer Kit (JDK) using &lt;code&gt;apt&lt;/code&gt; . You'll install OpenJDK as well as official packages from Oracle. You'll then select the version you wish to use for your projects. When you're finished, you'll be able to use the JDK to develop software or use the Java Runtime to run software.&lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;To follow this tutorial, you will need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One Debian 9 server set up by following the &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-debian-9"&gt;the Debian 9 initial server setup guide&lt;/a&gt; tutorial,  including a non-root user with &lt;code&gt;sudo&lt;/code&gt; access and a firewall.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="installing-the-default-jre-jdk"&gt;Installing the Default JRE/JDK&lt;/h2&gt;

&lt;p&gt;The easiest option for installing Java is to use the version packaged with Debian. By default, Debian 9 includes Open JDK,  which is an open-source variant of the JRE and JDK.&lt;/p&gt;

&lt;p&gt;This package will install OpenJDK version 1.8, which is compatible with Java 8. Java 8 is the current Long Term Support version and is still widely supported, though public maintenance ends in January 2019.&lt;/p&gt;

&lt;p&gt;To install this version, first update the package index:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt update
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, check if Java is already installed:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;java -version
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If Java is not currently installed, you'll see the following output:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;-bash: java: command not found
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Execute the following command to install OpenJDK:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install default-jre
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This command will install the Java Runtime Environment (JRE). This will allow you to run almost all Java software. &lt;/p&gt;

&lt;p&gt;Verify the installation with:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;java -version
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You'll see the following output:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;openjdk version "1.8.0_181"
OpenJDK Runtime Environment (build 1.8.0_181-8u181-b13-1~deb9u1-b13)
OpenJDK 64-Bit Server VM (build 25.181-b13, mixed mode)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You may need the Java Development Kit (JDK) in addition to the JRE in order to compile and run some specific Java-based software. To install the JDK, execute the following command, which will also install the JRE:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install default-jdk
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Verify that the JDK is installed by checking the version of &lt;code&gt;javac&lt;/code&gt;, the Java compiler:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;javac -version
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You'll see the following output:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;javac 1.8.0_181
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, let's look at how to install Oracle's official JDK and JRE.&lt;/p&gt;

&lt;h2 id="installing-the-oracle-jdk"&gt;Installing the Oracle JDK&lt;/h2&gt;

&lt;p&gt;If you want to install the Oracle JDK, which is the official version distributed by Oracle, you'll need to add a new package repository for the version you'd like to use. &lt;/p&gt;

&lt;p&gt;First, install the &lt;code&gt;software-properties-common&lt;/code&gt; package which adds the &lt;code&gt;apt-get-repository&lt;/code&gt; command which you'll use to add additional repositories to your sources list.&lt;/p&gt;

&lt;p&gt;Install &lt;code&gt;software-properties-common&lt;/code&gt; with:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install software-properties-common
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With this installed, you can install Oracle's Java.&lt;/p&gt;

&lt;h3 id="installing-oracle-java-8"&gt;Installing Oracle Java 8&lt;/h3&gt;

&lt;p&gt;To install Java 8, which is the current long-term support version,  first add its package repository:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo add-apt-repository ppa:webupd8team/java
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When you add the repository, you'll see a message like this:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="output"&gt;output&lt;/div&gt; Oracle Java (JDK) Installer (automatically downloads and installs Oracle JDK8). There are no actual Java files in this PPA.

Important -&amp;gt; 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.04, 17.10, 16.04, 14.04 and 12.04.

More info (and Ubuntu installation instructions):
- for Oracle Java 8: 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 10, see a different PPA: https://www.linuxuprising.com/2018/04/install-oracle-java-10-in-ubuntu-or.html
 More info: https://launchpad.net/~webupd8team/+archive/ubuntu/java
Press [ENTER] to continue or ctrl-c to cancel adding it
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Press &lt;code&gt;ENTER&lt;/code&gt; to continue.  It will attempt to import some GPG signing keys, but it won't be able to find any valid ones:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;gpg: keybox '/tmp/tmpgt9wdvth/pubring.gpg' created
gpg: /tmp/tmpgt9wdvth/trustdb.gpg: trustdb created
gpg: key C2518248EEA14886: public key "Launchpad VLC" imported
gpg: no ultimately trusted keys found
gpg: Total number processed: 1
gpg:               imported: 1
gpg: no valid OpenPGP data found.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Execute the following command to add the GPG key for the repository source manually:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;apt-key adv --keyserver keyserver.ubuntu.com --recv-keys C2518248EEA14886
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then update your package list:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt update
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once the package list updates, install Java 8:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install oracle-java8-installer
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Your system will download the JDK from Oracle and ask you to accept the license agreement. Accept the agreement and the JDK will install.&lt;/p&gt;

&lt;h3 id="installing-oracle-java-10"&gt;Installing Oracle Java 10&lt;/h3&gt;

&lt;p&gt;To install Oracle Java 10, first add its repository:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo add-apt-repository ppa:linuxuprising/java
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You'll see this message:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt; Oracle Java 10 installer

Java binaries are not hosted in this PPA due to licensing. The packages in this PPA download and install Oracle Java 10 (JDK 10), so a working Internet connection is required.

The packages in this PPA are based on the WebUpd8 Oracle Java PPA packages: https://launchpad.net/~webupd8team/+archive/ubuntu/java

Created for users of https://www.linuxuprising.com/

Issues or suggestions? Leave a comment here: https://www.linuxuprising.com/2018/04/install-oracle-java-10-in-ubuntu-or.html
 More info: https://launchpad.net/~linuxuprising/+archive/ubuntu/java
Press [ENTER] to continue or ctrl-c to cancel adding it
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Press &lt;code&gt;ENTER&lt;/code&gt; to continue the installation. Like with Java 8, you'll see a message about invalid signing keys:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;gpg: keybox '/tmp/tmpvuqsh9ui/pubring.gpg' created
gpg: /tmp/tmpvuqsh9ui/trustdb.gpg: trustdb created
gpg: key EA8CACC073C3DB2A: public key "Launchpad PPA for Linux Uprising" imported
gpg: Total number processed: 1
gpg:               imported: 1
gpg: no valid OpenPGP data found.

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Execute this command to import the necessary key:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys EA8CACC073C3DB2A
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then update your package list:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt update
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once the package list updates, install Java 10:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install oracle-java10-installer
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Your system will download the JDK from Oracle and ask you to accept the license agreement. Accept the agreement and the JDK will install.&lt;/p&gt;

&lt;p&gt;Now let's look at how to select which version of Java you want to use.&lt;/p&gt;

&lt;h2 id="managing-java"&gt;Managing Java&lt;/h2&gt;

&lt;p&gt;You can have multiple Java installations on one server. You can configure which version is the default for use on the command line by using the &lt;code&gt;update-alternatives&lt;/code&gt; command.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo update-alternatives --config java
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is what the output would look like if you've installed all versions of Java in this tutorial:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;There are 3 choices for the alternative java (providing /usr/bin/java).

  Selection    Path                                            Priority   Status
------------------------------------------------------------
  0            /usr/lib/jvm/java-10-oracle/bin/java             1091      auto mode
* 1            /usr/lib/jvm/java-10-oracle/bin/java             1091      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 &amp;lt;enter&amp;gt; to keep the current choice[*], or type selection number:
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Choose the number associated with the Java version to use it as the default, or press &lt;code&gt;ENTER&lt;/code&gt; to leave the current settings in place.&lt;/p&gt;

&lt;p&gt;You can do this for other Java commands, such as the compiler (&lt;code&gt;javac&lt;/code&gt;):&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo update-alternatives --config &lt;span class="highlight"&gt;javac&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Other commands for which this command can be run include, but are not limited to: &lt;code&gt;keytool&lt;/code&gt;, &lt;code&gt;javadoc&lt;/code&gt; and &lt;code&gt;jarsigner&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let's set the &lt;code&gt;JAVA_HOME&lt;/code&gt; environment variable next.&lt;/p&gt;

&lt;h2 id="setting-the-java_home-environment-variable"&gt;Setting the &lt;code&gt;JAVA_HOME&lt;/code&gt; Environment Variable&lt;/h2&gt;

&lt;p&gt;Many programs written using Java use the &lt;code&gt;JAVA_HOME&lt;/code&gt; environment variable to determine the Java installation location. &lt;/p&gt;

&lt;p&gt;To set this environment variable,  first determine where Java is installed. Use the &lt;code&gt;update-alternatives&lt;/code&gt; command again:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo update-alternatives --config java
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This command shows each installation of Java along with its installation path:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;  Selection    Path                                            Priority   Status
------------------------------------------------------------
  0            /usr/lib/jvm/java-10-oracle/bin/java             1091      auto mode
* 1            /usr/lib/jvm/java-10-oracle/bin/java             1091      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
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this case the installation paths are as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Oracle Java 10 is located at &lt;code&gt;/usr/lib/jvm/java-10-oracle/jre/bin/java&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Oracle Java 8 is located at &lt;code&gt;/usr/lib/jvm/java-8-oracle/jre/bin/java&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;OpenJDK 8 is located at &lt;code&gt;/usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These paths show the path to the &lt;code&gt;java&lt;/code&gt; executable. &lt;/p&gt;

&lt;p&gt;Copy the path for your preferred installation, excluding the trailing &lt;code&gt;bin/java&lt;/code&gt; component. Then open &lt;code&gt;/etc/environment&lt;/code&gt; using &lt;code&gt;nano&lt;/code&gt; or your favorite text editor:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/environment
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At the end of this file, add the following line, making sure to replace the highlighted path with your own copied path:&lt;/p&gt;
&lt;div class="code-label " title="/etc/environment"&gt;/etc/environment&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;JAVA_HOME="&lt;span class="highlight"&gt;/usr/lib/jvm/java-8-oracle/jre&lt;/span&gt;"
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Modifying this file will set the &lt;code&gt;JAVA_HOME&lt;/code&gt; path for all users on your system.&lt;/p&gt;

&lt;p&gt;Save the file and exit the editor.&lt;/p&gt;

&lt;p&gt;Now reload this file to apply the changes to your current session:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;source /etc/environment
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Verify that the environment variable is set:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;echo $JAVA_HOME
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You'll see the path you just set:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;&lt;span class="highlight"&gt;/usr/lib/jvm/java-8-oracle/jre&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Other users will need to execute the command &lt;code&gt;source /etc/environment&lt;/code&gt; or log out and log back in to apply this setting.&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;In this tutorial you installed multiple versions of Java and learned how to manage them. You can now install software which runs on Java, such as Tomcat, Jetty, Glassfish, Cassandra or Jenkins.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-install-docker-compose-on-debian-9</id>
    <published>2018-09-06T22:13:28Z</published>
    <updated>2018-09-06T22:14:16Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-install-docker-compose-on-debian-9"/>
    <title>How To Install Docker Compose on Debian 9</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://docs.docker.com/"&gt;Docker&lt;/a&gt; is a great tool for automating the deployment of Linux applications inside software containers, but to take full advantage of its potential each component of an application should run in its own individual container. For complex applications with a lot of components, orchestrating all the containers to start up, communicate, and shut down together can quickly become unwieldy.&lt;/p&gt;

&lt;p&gt;The Docker community came up with a popular solution called &lt;a href="http://www.fig.sh/"&gt;Fig&lt;/a&gt;, which allowed you to use a single YAML file to orchestrate all of your Docker containers and configurations. This became so popular that the Docker team decided to make &lt;a href="https://docs.docker.com/compose/"&gt;Docker Compose&lt;/a&gt; based on the Fig source, which is now deprecated. Docker Compose makes it easier for users to orchestrate the processes of Docker containers, including starting up, shutting down, and setting up intra-container linking and volumes.&lt;/p&gt;

&lt;p&gt;In this tutorial, we'll show you how to install the latest version of Docker Compose to help you manage multi-container applications on a Debian 9 server.&lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;To follow this article, you will need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Debian 9 server and a non-root user with sudo privileges. This &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-18-04"&gt;initial server setup with Debian 9 tutorial&lt;/a&gt; explains how to set this up.&lt;/li&gt;
&lt;li&gt; Docker installed with the instructions from &lt;strong&gt;Step 1&lt;/strong&gt;  and &lt;strong&gt;Step 2&lt;/strong&gt; of &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-and-use-docker-on-debian-9"&gt;How To Install and Use Docker on Debian 9&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;span class='note'&gt;&lt;strong&gt;Note:&lt;/strong&gt; Even though the Prerequisites give instructions for installing Docker on Debian 9, the &lt;code&gt;docker&lt;/code&gt; commands in this article should work on other operating systems as long as Docker is installed.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;

&lt;h2 id="step-1-—-installing-docker-compose"&gt;Step 1 — Installing Docker Compose&lt;/h2&gt;

&lt;p&gt;Although we can install Docker Compose from the official Debian repositories, it is several minor versions behind the latest release, so we'll install it from Docker's GitHub repository.  The command below is slightly different than the one you'll find on the &lt;a href="https://github.com/docker/compose/releases"&gt;Releases&lt;/a&gt; page. By using the &lt;code&gt;-o&lt;/code&gt; flag to specify the output file first rather than redirecting the output, this syntax avoids running into a permission denied error caused when using &lt;code&gt;sudo&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We'll check the &lt;a href="https://github.com/docker/compose/releases"&gt;current release&lt;/a&gt; and, if necessary, update it in the command below:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo curl -L https://github.com/docker/compose/releases/download/&lt;span class="highlight"&gt;1.22.0&lt;/span&gt;/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next we'll set the permissions:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo chmod +x /usr/local/bin/docker-compose
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then we'll verify that the installation was successful by checking the version:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;docker-compose --version
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will print out the version we installed:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;docker-compose version &lt;span class="highlight"&gt;1.22.0&lt;/span&gt;, build f46880fe
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that we have Docker Compose installed, we're ready to run a "Hello World" example.&lt;/p&gt;

&lt;h2 id="step-2-—-running-a-container-with-docker-compose"&gt;Step 2 — Running a Container with Docker Compose&lt;/h2&gt;

&lt;p&gt;The public Docker registry, Docker Hub, includes a &lt;em&gt;Hello World&lt;/em&gt; image for demonstration and testing. It illustrates the minimal configuration required to run a container using Docker Compose: a YAML file that calls a single image. We'll create this minimal configuration to run our &lt;code&gt;hello-world&lt;/code&gt; container.&lt;/p&gt;

&lt;p&gt;First, we'll create a directory for the YAML file and move into it:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;mkdir hello-world
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;cd hello-world
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, we'll create the YAML file:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nano docker-compose.yml
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Put the following contents into the file, save the file, and exit the text editor:&lt;/p&gt;
&lt;div class="code-label " title="docker-compose.yml"&gt;docker-compose.yml&lt;/div&gt;&lt;pre class="code-pre yml"&gt;&lt;code langs=""&gt;my-test:
 image: hello-world
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first line in the YAML file is used as part of the container name. The second line specifies which image to use to create the container. When we run the &lt;code&gt;docker-compose up&lt;/code&gt; command, it will look for a local image by the name we specified, &lt;code&gt;hello-world&lt;/code&gt;. With this in place, we’ll save and exit the file.&lt;/p&gt;

&lt;p&gt;We can look manually at images on our system with the &lt;code&gt;docker images&lt;/code&gt; command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;docker images
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When there are no local images at all, only the column headings display:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, while still in the &lt;code&gt;~/hello-world&lt;/code&gt; directory, we'll execute the following command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;docker-compose up
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first time we run the command, if there's no local image named &lt;code&gt;hello-world&lt;/code&gt;, Docker Compose will pull it from the Docker Hub public repository:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Pulling my-test (hello-world:)...
latest: Pulling from library/hello-world
9db2ca6ccae0: Pull complete
Digest: sha256:4b8ff392a12ed9ea17784bd3c9a8b1fa3299cac44aca35a85c90c5e3c7afacdc
Status: Downloaded newer image for hello-world:latest
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After pulling the image, &lt;code&gt;docker-compose&lt;/code&gt; creates a container, attaches, and runs the &lt;a href="https://github.com/docker-library/hello-world/blob/85fd7ab65e079b08019032479a3f306964a28f4d/hello-world/Dockerfile"&gt;hello&lt;/a&gt; program, which in turn confirms that the installation appears to be working:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;. . .
Creating helloworld_my-test_1...
Attaching to helloworld_my-test_1
my-test_1 |
my-test_1 | Hello from Docker.
my-test_1 | This message shows that your installation appears to be working correctly.
my-test_1 |
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then it prints an explanation of what it did:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt; To generate this message, Docker took the following steps:
my-test_1  |  1. The Docker client contacted the Docker daemon.
my-test_1  |  2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
my-test_1  |     (amd64)
my-test_1  |  3. The Docker daemon created a new container from that image which runs the
my-test_1  |     executable that produces the output you are currently reading.
my-test_1  |  4. The Docker daemon streamed that output to the Docker client, which sent it
my-test_1  |     to your terminal.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Docker containers only run as long as the command is active, so once &lt;code&gt;hello&lt;/code&gt; finished running, the container stopped.  Consequently, when we look at active processes, the column headers will appear, but the &lt;code&gt;hello-world&lt;/code&gt; container won't be listed because it's not running:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;docker ps
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                      PORTS               NAMES
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can see the container information, which we'll need in the next step, by using the &lt;code&gt;-a&lt;/code&gt; flag. This shows all containers, not just active ones:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;docker ps -a
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                      PORTS               NAMES
06069fd5ca23        hello-world         "/hello"            35 minutes ago      Exited (0) 35 minutes ago                       hello-world_my-test_1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This displays the information we'll need to remove the container when we're done with it.&lt;/p&gt;

&lt;h2 id="step-3-—-removing-the-image-optional"&gt;Step 3 — Removing the Image (Optional)&lt;/h2&gt;

&lt;p&gt;To avoid using unnecessary disk space, we'll remove the local image. To do so, we'll need to delete all the containers that reference the image using the &lt;code&gt;docker rm&lt;/code&gt; command, followed by either the &lt;code&gt;CONTAINER ID&lt;/code&gt; or the &lt;code&gt;NAME&lt;/code&gt;. Below, we're using the &lt;code&gt;CONTAINER ID&lt;/code&gt; from the &lt;code&gt;docker ps -a&lt;/code&gt; command we just ran. Be sure to substitute the ID of your container:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;docker rm &lt;span class="highlight"&gt;06069fd5ca23&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once all containers that reference the image have been removed, we can remove the image:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;docker rmi hello-world
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;We've now installed Docker Compose, tested our installation by running a Hello World example, and removed the test image and container.&lt;/p&gt;

&lt;p&gt;While the Hello World example confirmed our installation, the simple configuration does not show one of the main benefits of Docker Compose — being able to bring a group of Docker containers up and down all at the same time.  To see the power of Docker Compose in action, you might like to check out this practical example, &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-configure-a-continuous-integration-testing-environment-with-docker-and-docker-compose-on-ubuntu-16-04"&gt;How To Configure a Continuous Integration Testing Environment with Docker and Docker Compose on Ubuntu 16.04&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-set-up-a-firewall-with-ufw-on-debian-9</id>
    <published>2018-09-06T20:28:19Z</published>
    <updated>2018-09-06T20:30:26Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-set-up-a-firewall-with-ufw-on-debian-9"/>
    <title>How To Set Up a Firewall with UFW on Debian 9</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;UFW, or Uncomplicated Firewall, is an interface to &lt;code&gt;iptables&lt;/code&gt; that is geared towards simplifying the process of configuring a firewall. While &lt;code&gt;iptables&lt;/code&gt; is a solid and flexible tool, it can be difficult for beginners to learn how to use it to properly configure a firewall. If you're looking to get started securing your network, and you're not sure which tool to use, UFW may be the right choice for you.&lt;/p&gt;

&lt;p&gt;This tutorial will show you how to set up a firewall with UFW on Debian 9.&lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;To follow this tutorial, you will need One Debian 9 server with a sudo non-root user, which you can set up by following Steps 1–3 in the &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-debian-9"&gt;Initial Server Setup with Debian 9 tutorial&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id="step-1-–-installing-ufw"&gt;Step 1 – Installing UFW&lt;/h2&gt;

&lt;p&gt;Debian does not install UFW by default. If you followed through the entire &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-debian-9"&gt;Initial Server Setup tutorial&lt;/a&gt; you will have already installed and enabled UFW. If not, install it now using &lt;code&gt;apt&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install ufw
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We will set up UFW and enable it in the following steps.&lt;/p&gt;

&lt;h2 id="step-2-—-using-ipv6-with-ufw-optional"&gt;Step 2 — Using IPv6 with UFW (Optional)&lt;/h2&gt;

&lt;p&gt;This tutorial is written with IPv4 in mind, but will work for IPv6 as well as long as you enable it. If your Debian server has IPv6 enabled, ensure that UFW is configured to support IPv6 so that it will manage firewall rules for IPv6 in addition to IPv4. To do this, open the UFW configuration with &lt;code&gt;nano&lt;/code&gt; or your favorite editor.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/default/ufw
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then make sure the value of &lt;code&gt;IPV6&lt;/code&gt; is &lt;code&gt;yes&lt;/code&gt;. It should look like this:&lt;/p&gt;
&lt;div class="code-label " title="/etc/default/ufw excerpt"&gt;/etc/default/ufw excerpt&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;IPV6=&lt;span class="highlight"&gt;yes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save and close the file. Now, when UFW is enabled, it will be configured to write both IPv4 and IPv6 firewall rules. However, before enabling UFW, we will want to ensure that your firewall is configured to allow you to connect via SSH. Let's start with setting the default policies.&lt;/p&gt;

&lt;h2 id="step-3-—-setting-up-default-policies"&gt;Step 3 — Setting Up Default Policies&lt;/h2&gt;

&lt;p&gt;If you're just getting started with your firewall, the first rules to define are your default policies. These rules control how to handle traffic that does not explicitly match any other rules. By default, UFW is set to deny all incoming connections and allow all outgoing connections. This means anyone trying to reach your server would not be able to connect, while any application within the server would be able to reach the outside world.&lt;/p&gt;

&lt;p&gt;Let's set your UFW rules back to the defaults so we can be sure that you'll be able to follow along with this tutorial. To set the defaults used by UFW, use these commands:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw default deny incoming
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo ufw default allow outgoing
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These commands set the defaults to deny incoming and allow outgoing connections. These firewall defaults alone might suffice for a personal computer, but servers typically need to respond to incoming requests from outside users. We'll look into that next.&lt;/p&gt;

&lt;h2 id="step-4-—-allowing-ssh-connections"&gt;Step 4 — Allowing SSH Connections&lt;/h2&gt;

&lt;p&gt;If we enabled our UFW firewall now, it would deny all incoming connections. This means that we will need to create rules that explicitly allow legitimate incoming connections — SSH or HTTP connections, for example — if we want our server to respond to those types of requests. If you're using a cloud server, you will probably want to allow incoming SSH connections so you can connect to and manage your server.&lt;/p&gt;

&lt;p&gt;To configure your server to allow incoming SSH connections, you can use this command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw allow ssh
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will create firewall rules that will allow all connections on port &lt;code&gt;22&lt;/code&gt;, which is the port that the SSH daemon listens on by default. UFW knows what port &lt;code&gt;allow ssh&lt;/code&gt; means because it's listed as a service in the &lt;code&gt;/etc/services&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;However, we can actually write the equivalent rule by specifying the port instead of the service name. For example, this command works the same as the one above:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw allow 22
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you configured your SSH daemon to use a different port, you will have to specify the appropriate port. For example, if your SSH server is listening on port &lt;code&gt;2222&lt;/code&gt;, you can use this command to allow connections on that port:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw allow &lt;span class="highlight"&gt;2222&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that your firewall is configured to allow incoming SSH connections, we can enable it.&lt;/p&gt;

&lt;h2 id="step-5-—-enabling-ufw"&gt;Step 5 — Enabling UFW&lt;/h2&gt;

&lt;p&gt;To enable UFW, use this command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw enable
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will receive a warning that says the command may disrupt existing SSH connections. We already set up a firewall rule that allows SSH connections, so it should be fine to continue. Respond to the prompt with &lt;code&gt;y&lt;/code&gt; and hit &lt;code&gt;ENTER&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The firewall is now active. Run the &lt;code&gt;sudo ufw status verbose&lt;/code&gt; command to see the rules that are set. The rest of this tutorial covers how to use UFW in more detail, like allowing or denying different kinds of connections.&lt;/p&gt;

&lt;h2 id="step-6-—-allowing-other-connections"&gt;Step 6 — Allowing Other Connections&lt;/h2&gt;

&lt;p&gt;At this point, you should allow all of the other connections that your server needs to respond to. The connections that you should allow depends on your specific needs. Luckily, you already know how to write rules that allow connections based on a service name or port; we already did this for SSH on port &lt;code&gt;22&lt;/code&gt;. You can also do this for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  HTTP on port 80, which is what unencrypted web servers use, using &lt;code&gt;sudo ufw allow http&lt;/code&gt; or &lt;code&gt;sudo ufw allow 80&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;  HTTPS on port 443, which is what encrypted web servers use, using &lt;code&gt;sudo ufw allow https&lt;/code&gt; or &lt;code&gt;sudo ufw allow 443&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are several others ways to allow other connections, aside from specifying a port or known service.&lt;/p&gt;

&lt;h3 id="specific-port-ranges"&gt;Specific Port Ranges&lt;/h3&gt;

&lt;p&gt;You can specify port ranges with UFW. Some applications use multiple ports, instead of a single port.&lt;/p&gt;

&lt;p&gt;For example, to allow X11 connections, which use ports &lt;code&gt;6000&lt;/code&gt;-&lt;code&gt;6007&lt;/code&gt;, use these commands:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw allow &lt;span class="highlight"&gt;6000&lt;/span&gt;:&lt;span class="highlight"&gt;6007&lt;/span&gt;/tcp
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo ufw allow &lt;span class="highlight"&gt;6000&lt;/span&gt;:&lt;span class="highlight"&gt;6007&lt;/span&gt;/udp
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When specifying port ranges with UFW, you must specify the protocol (&lt;code&gt;tcp&lt;/code&gt; or &lt;code&gt;udp&lt;/code&gt;) that the rules should apply to. We haven't mentioned this before because not specifying the protocol automatically allows both protocols, which is OK in most cases.&lt;/p&gt;

&lt;h3 id="specific-ip-addresses"&gt;Specific IP Addresses&lt;/h3&gt;

&lt;p&gt;When working with UFW, you can also specify IP addresses. For example, if you want to allow connections from a specific IP address, such as a work or home IP address of &lt;code&gt;203.0.113.4&lt;/code&gt;, you need to specify &lt;code&gt;from&lt;/code&gt;, then the IP address:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw allow from &lt;span class="highlight"&gt;203.0.113.4&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can also specify a specific port that the IP address is allowed to connect to by adding &lt;code&gt;to any port&lt;/code&gt; followed by the port number. For example, If you want to allow &lt;code&gt;203.0.113.4&lt;/code&gt; to connect to port &lt;code&gt;22&lt;/code&gt; (SSH), use this command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw allow from &lt;span class="highlight"&gt;203.0.113.4&lt;/span&gt; to any port &lt;span class="highlight"&gt;22&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="subnets"&gt;Subnets&lt;/h3&gt;

&lt;p&gt;If you want to allow a subnet of IP addresses, you can do so using CIDR notation to specify a netmask. For example, if you want to allow all of the IP addresses ranging from &lt;code&gt;203.0.113.1&lt;/code&gt; to &lt;code&gt;203.0.113.254&lt;/code&gt; you could use this command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw allow from &lt;span class="highlight"&gt;203.0.113.0&lt;/span&gt;/&lt;span class="highlight"&gt;24&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Likewise, you may also specify the destination port that the subnet &lt;code&gt;203.0.113.0/24&lt;/code&gt; is allowed to connect to. Again, we'll use port &lt;code&gt;22&lt;/code&gt; (SSH) as an example:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw allow from &lt;span class="highlight"&gt;203.0.113.0&lt;/span&gt;/&lt;span class="highlight"&gt;24&lt;/span&gt; to any port 22
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="connections-to-a-specific-network-interface"&gt;Connections to a Specific Network Interface&lt;/h3&gt;

&lt;p&gt;If you want to create a firewall rule that only applies to a specific network interface, you can do so by specifying "allow in on" followed by the name of the network interface.&lt;/p&gt;

&lt;p&gt;You may want to look up your network interfaces before continuing. To do so, use this command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;ip addr
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output Excerpt"&gt;Output Excerpt&lt;/div&gt;2: &lt;span class="highlight"&gt;eth0&lt;/span&gt;: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1500 qdisc pfifo_fast state
. . .
3: &lt;span class="highlight"&gt;eth1&lt;/span&gt;: &amp;lt;BROADCAST,MULTICAST&amp;gt; mtu 1500 qdisc noop state DOWN group default
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The highlighted output indicates the network interface names. They are typically named something like &lt;code&gt;eth0&lt;/code&gt; or &lt;code&gt;enp3s2&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So, if your server has a public network interface called &lt;code&gt;eth0&lt;/code&gt;, you could allow HTTP traffic (port &lt;code&gt;80&lt;/code&gt;) to it with this command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw allow in on &lt;span class="highlight"&gt;eth0&lt;/span&gt; to any port &lt;span class="highlight"&gt;80&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Doing so would allow your server to receive HTTP requests from the public internet.&lt;/p&gt;

&lt;p&gt;Or, if you want your MySQL database server (port &lt;code&gt;3306&lt;/code&gt;) to listen for connections on the private network interface &lt;code&gt;eth1&lt;/code&gt;, for example, you could use this command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw allow in on &lt;span class="highlight"&gt;eth1&lt;/span&gt; to any port &lt;span class="highlight"&gt;3306&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This would allow other servers on your private network to connect to your MySQL database.&lt;/p&gt;

&lt;h2 id="step-7-—-denying-connections"&gt;Step 7 — Denying Connections&lt;/h2&gt;

&lt;p&gt;If you haven't changed the default policy for incoming connections, UFW is configured to deny all incoming connections. Generally, this simplifies the process of creating a secure firewall policy by requiring you to create rules that explicitly allow specific ports and IP addresses through.&lt;/p&gt;

&lt;p&gt;However, sometimes you will want to deny specific connections based on the source IP address or subnet, perhaps because you know that your server is being attacked from there. Also, if you want to change your default incoming policy to &lt;strong&gt;allow&lt;/strong&gt; (which is not recommended), you would need to create &lt;strong&gt;deny&lt;/strong&gt; rules for any services or IP addresses that you don't want to allow connections for.&lt;/p&gt;

&lt;p&gt;To write &lt;strong&gt;deny&lt;/strong&gt; rules, you can use the commands described above, replacing &lt;strong&gt;allow&lt;/strong&gt; with &lt;strong&gt;deny&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For example, to deny HTTP connections, you could use this command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw deny http
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or if you want to deny all connections from &lt;code&gt;203.0.113.4&lt;/code&gt; you could use this command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw deny from &lt;span class="highlight"&gt;203.0.113.4&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now let's take a look at how to delete rules.&lt;/p&gt;

&lt;h2 id="step-8-—-deleting-rules"&gt;Step 8 — Deleting Rules&lt;/h2&gt;

&lt;p&gt;Knowing how to delete firewall rules is just as important as knowing how to create them. There are two different ways to specify which rules to delete: by rule number or by the actual rule (similar to how the rules were specified when they were created). We'll start with the &lt;strong&gt;delete by rule number&lt;/strong&gt; method because it is easier.&lt;/p&gt;

&lt;h3 id="by-rule-number"&gt;By Rule Number&lt;/h3&gt;

&lt;p&gt;If you're using the rule number to delete firewall rules, the first thing you'll want to do is get a list of your firewall rules. The UFW status command has an option to display numbers next to each rule, as demonstrated here:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw status numbered
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Numbered Output:"&gt;Numbered Output:&lt;/div&gt;Status: active

     To                         Action      From
     --                         ------      ----
[ 1] 22                         ALLOW IN    15.15.15.0/24
[ 2] 80                         ALLOW IN    Anywhere
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If we decide that we want to delete rule 2, the one that allows port 80 (HTTP) connections, we can specify it in a UFW delete command like this:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw delete &lt;span class="highlight"&gt;2&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This would show a confirmation prompt then delete rule 2, which allows HTTP connections. Note that if you have IPv6 enabled, you would want to delete the corresponding IPv6 rule as well.&lt;/p&gt;

&lt;h3 id="by-actual-rule"&gt;By Actual Rule&lt;/h3&gt;

&lt;p&gt;The alternative to rule numbers is to specify the actual rule to delete. For example, if you want to remove the &lt;code&gt;allow http&lt;/code&gt; rule, you could write it like this:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw delete &lt;span class="highlight"&gt;allow http&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You could also specify the rule by &lt;code&gt;allow 80&lt;/code&gt;, instead of by service name:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw delete &lt;span class="highlight"&gt;allow 80&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This method will delete both IPv4 and IPv6 rules, if they exist.&lt;/p&gt;

&lt;h2 id="step-9-—-checking-ufw-status-and-rules"&gt;Step 9 — Checking UFW Status and Rules&lt;/h2&gt;

&lt;p&gt;At any time, you can check the status of UFW with this command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw status verbose
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If UFW is disabled, which it is by default, you'll see something like this:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Status: inactive
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If UFW is active, which it should be if you followed Step 3, the output will say that it's active and it will list any rules that are set. For example, if the firewall is set to allow SSH (port &lt;code&gt;22&lt;/code&gt;) connections from anywhere, the output might look something like this:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
New profiles: skip

To                         Action      From
--                         ------      ----
22/tcp                     ALLOW IN    Anywhere
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Use the &lt;code&gt;status&lt;/code&gt; command if you want to check how UFW has configured the firewall.&lt;/p&gt;

&lt;h2 id="step-10-—-disabling-or-resetting-ufw-optional"&gt;Step 10 — Disabling or Resetting UFW (optional)&lt;/h2&gt;

&lt;p&gt;If you decide you don't want to use UFW, you can disable it with this command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw disable
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Any rules that you created with UFW will no longer be active. You can always run &lt;code&gt;sudo ufw enable&lt;/code&gt; if you need to activate it later.&lt;/p&gt;

&lt;p&gt;If you already have UFW rules configured but you decide that you want to start over, you can use the reset command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw reset
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will disable UFW and delete any rules that were previously defined. Keep in mind that the default policies won't change to their original settings, if you modified them at any point. This should give you a fresh start with UFW.&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Your firewall is now configured to allow (at least) SSH connections. Be sure to allow any other incoming connections that your server, while limiting any unnecessary connections, so your server will be functional and secure.&lt;/p&gt;

&lt;p&gt;To learn about more common UFW configurations, check out the &lt;a href="https://www.digitalocean.com/community/tutorials/ufw-essentials-common-firewall-rules-and-commands"&gt;UFW Essentials: Common Firewall Rules and Commands&lt;/a&gt; tutorial.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-set-up-django-with-postgres-nginx-and-gunicorn-on-debian-9</id>
    <published>2018-09-06T19:43:12Z</published>
    <updated>2018-09-07T20:01:59Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-set-up-django-with-postgres-nginx-and-gunicorn-on-debian-9"/>
    <title>How To Set Up Django with Postgres, Nginx, and Gunicorn on Debian 9</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;Django is a powerful web framework that can help you get your Python application or website off the ground.  Django includes a simplified development server for testing your code locally, but for anything even slightly production related, a more secure and powerful web server is required.&lt;/p&gt;

&lt;p&gt;In this guide, we will demonstrate how to install and configure some components on Debian 9 to support and serve Django applications.  We will be setting up a PostgreSQL database instead of using the default SQLite database.  We will configure the Gunicorn application server to interface with our applications.  We will then set up Nginx to reverse proxy to Gunicorn, giving us access to its security and performance features to serve our apps.&lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;In order to complete this guide, you should have a fresh Debian 9 server instance with a basic firewall and a non-root user with &lt;code&gt;sudo&lt;/code&gt; privileges configured.  You can learn how to set this up by running through our &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-debian-9"&gt;initial server setup guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We will be installing Django within a virtual environment.  Installing Django into an environment specific to your project will allow your projects and their requirements to be handled separately.&lt;/p&gt;

&lt;p&gt;Once we have our database and application up and running, we will install and configure the Gunicorn application server.  This will serve as an interface to our application, translating client requests from HTTP to Python calls that our application can process.  We will then set up Nginx in front of Gunicorn to take advantage of its high performance connection handling mechanisms and its easy-to-implement security features.&lt;/p&gt;

&lt;p&gt;Let's get started.&lt;/p&gt;

&lt;h2 id="step-1-—-installing-the-packages-from-the-debian-repositories"&gt;Step 1 — Installing the Packages from the Debian Repositories&lt;/h2&gt;

&lt;p&gt;To begin the process, we'll download and install all of the items we need from the Debian repositories.  We will use the Python package manager &lt;code&gt;pip&lt;/code&gt; to install additional components a bit later.&lt;/p&gt;

&lt;p&gt;We need to update the local &lt;code&gt;apt&lt;/code&gt; package index and then download and install the packages.  The packages we install depend on which version of Python your project will use.&lt;/p&gt;

&lt;p&gt;If you are using Django with &lt;strong&gt;Python 3&lt;/strong&gt;, type:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt update
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo apt install python3-pip python3-dev libpq-dev postgresql postgresql-contrib nginx curl
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Django 1.11 is the last release of Django that will support Python 2.  If you are starting new projects, it is strongly recommended that you choose Python 3.  If you still need to use &lt;strong&gt;Python 2&lt;/strong&gt;, type:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt update
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo apt install python-pip python-dev libpq-dev postgresql postgresql-contrib nginx curl
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will install &lt;code&gt;pip&lt;/code&gt;, the Python development files needed to build Gunicorn later, the Postgres database system and the libraries needed to interact with it, and the Nginx web server.&lt;/p&gt;

&lt;h2 id="step-2-—-creating-the-postgresql-database-and-user"&gt;Step 2 — Creating the PostgreSQL Database and User&lt;/h2&gt;

&lt;p&gt;We're going to jump right in and create a database and database user for our Django application.&lt;/p&gt;

&lt;p&gt;By default, Postgres uses an authentication scheme called "peer authentication" for local connections. Basically, this means that if the user's operating system username matches a valid Postgres username, that user can login with no further authentication.&lt;/p&gt;

&lt;p&gt;During the Postgres installation, an operating system user named &lt;code&gt;postgres&lt;/code&gt; was created to correspond to the &lt;code&gt;postgres&lt;/code&gt; PostgreSQL administrative user. We need to use this user to perform administrative tasks. We can use sudo and pass in the username with the &lt;code&gt;-u&lt;/code&gt; option.&lt;/p&gt;

&lt;p&gt;Log into an interactive Postgres session by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo -u postgres psql
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will be given a PostgreSQL prompt where we can set up our requirements.&lt;/p&gt;

&lt;p&gt;First, create a database for your project:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="postgres=#"&gt;CREATE DATABASE &lt;span class="highlight"&gt;myproject&lt;/span&gt;;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span class='note'&gt;&lt;strong&gt;Note:&lt;/strong&gt; Every Postgres statement must end with a semi-colon, so make sure that your command ends with one if you are experiencing issues.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;Next, create a database user for our project.  Make sure to select a secure password:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="postgres=#"&gt;CREATE USER &lt;span class="highlight"&gt;myprojectuser&lt;/span&gt; WITH PASSWORD '&lt;span class="highlight"&gt;password&lt;/span&gt;';
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Afterwards, we'll modify a few of the connection parameters for the user we just created. This will speed up database operations so that the correct values do not have to be queried and set each time a connection is established.&lt;/p&gt;

&lt;p&gt;We are setting the default encoding to &lt;code&gt;UTF-8&lt;/code&gt;, which Django expects. We are also setting the default transaction isolation scheme to "read committed", which blocks reads from uncommitted transactions. Lastly, we are setting the timezone. By default, our Django projects will be set to use &lt;code&gt;UTC&lt;/code&gt;. These are all recommendations from &lt;a href="https://docs.djangoproject.com/en/2.0/ref/databases/#optimizing-postgresql-s-configuration"&gt;the Django project itself&lt;/a&gt;:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="postgres=#"&gt;ALTER ROLE &lt;span class="highlight"&gt;myprojectuser&lt;/span&gt; SET client_encoding TO 'utf8';
&lt;/li&gt;&lt;li class="line" prefix="postgres=#"&gt;ALTER ROLE &lt;span class="highlight"&gt;myprojectuser&lt;/span&gt; SET default_transaction_isolation TO 'read committed';
&lt;/li&gt;&lt;li class="line" prefix="postgres=#"&gt;ALTER ROLE &lt;span class="highlight"&gt;myprojectuser&lt;/span&gt; SET timezone TO 'UTC';
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, we can give our new user access to administer our new database:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="postgres=#"&gt;GRANT ALL PRIVILEGES ON DATABASE &lt;span class="highlight"&gt;myproject&lt;/span&gt; TO &lt;span class="highlight"&gt;myprojectuser&lt;/span&gt;;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When you are finished, exit out of the PostgreSQL prompt by typing:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="postgres=#"&gt;\q
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Postgres is now set up so that Django can connect to and manage its database information.&lt;/p&gt;

&lt;h2 id="step-3-—-creating-a-python-virtual-environment-for-your-project"&gt;Step 3 — Creating a Python Virtual Environment for your Project&lt;/h2&gt;

&lt;p&gt;Now that we have our database, we can begin getting the rest of our project requirements ready.  We will be installing our Python requirements within a virtual environment for easier management.&lt;/p&gt;

&lt;p&gt;To do this, we first need access to the &lt;code&gt;virtualenv&lt;/code&gt; command.  We can install this with &lt;code&gt;pip&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you are using &lt;strong&gt;Python 3&lt;/strong&gt;, upgrade &lt;code&gt;pip&lt;/code&gt; and install the package by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo -H pip3 install --upgrade pip
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo -H pip3 install virtualenv
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you are using &lt;strong&gt;Python 2&lt;/strong&gt;, upgrade &lt;code&gt;pip&lt;/code&gt; and install the package by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo -H pip install --upgrade pip
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo -H pip install virtualenv
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With &lt;code&gt;virtualenv&lt;/code&gt; installed, we can start forming our project.  Create and move into a directory where we can keep our project files:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;mkdir ~/&lt;span class="highlight"&gt;myprojectdir&lt;/span&gt;
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;cd ~/&lt;span class="highlight"&gt;myprojectdir&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Within the project directory, create a Python virtual environment by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;virtualenv &lt;span class="highlight"&gt;myprojectenv&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will create a directory called &lt;code&gt;&lt;span class="highlight"&gt;myprojectenv&lt;/span&gt;&lt;/code&gt; within your &lt;code&gt;&lt;span class="highlight"&gt;myprojectdir&lt;/span&gt;&lt;/code&gt; directory.  Inside, it will install a local version of Python and a local version of &lt;code&gt;pip&lt;/code&gt;.  We can use this to install and configure an isolated Python environment for our project.&lt;/p&gt;

&lt;p&gt;Before we install our project's Python requirements, we need to activate the virtual environment.  You can do that by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;source &lt;span class="highlight"&gt;myprojectenv&lt;/span&gt;/bin/activate
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Your prompt should change to indicate that you are now operating within a Python virtual environment.  It will look something like this: &lt;code&gt;(&lt;span class="highlight"&gt;myprojectenv&lt;/span&gt;)&lt;span class="highlight"&gt;user&lt;/span&gt;@&lt;span class="highlight"&gt;host&lt;/span&gt;:~/&lt;span class="highlight"&gt;myprojectdir&lt;/span&gt;$&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;With your virtual environment active, install Django, Gunicorn, and the &lt;code&gt;psycopg2&lt;/code&gt; PostgreSQL adaptor with the local instance of &lt;code&gt;pip&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;span class='note'&gt;&lt;strong&gt;Note:&lt;/strong&gt; When the virtual environment is activated (when your prompt has &lt;code&gt;(myprojectenv)&lt;/code&gt; preceding it), use &lt;code&gt;pip&lt;/code&gt; instead of &lt;code&gt;pip3&lt;/code&gt;, even if you are using Python 3.  The virtual environment's copy of the tool is always named &lt;code&gt;pip&lt;/code&gt;, regardless of the Python version.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="(myprojectenv) $"&gt;pip install django gunicorn psycopg2-binary
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should now have all of the software needed to start a Django project.&lt;/p&gt;

&lt;h2 id="step-4-—-creating-and-configuring-a-new-django-project"&gt;Step 4 — Creating and Configuring a New Django Project&lt;/h2&gt;

&lt;p&gt;With our Python components installed, we can create the actual Django project files.&lt;/p&gt;

&lt;h3 id="creating-the-django-project"&gt;Creating the Django Project&lt;/h3&gt;

&lt;p&gt;Since we already have a project directory, we will tell Django to install the files here.  It will create a second level directory with the actual code, which is normal, and place a management script in this directory.  The key to this is that we are defining the directory explicitly instead of allowing Django to make decisions relative to our current directory:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="(myprojectenv) $"&gt;django-admin.py startproject &lt;span class="highlight"&gt;myproject&lt;/span&gt; ~/&lt;span class="highlight"&gt;myprojectdir&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At this point, your project directory (&lt;code&gt;~/&lt;span class="highlight"&gt;myprojectdir&lt;/span&gt;&lt;/code&gt; in our case) should have the following content:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;~/myprojectdir/manage.py&lt;/code&gt;: A Django project management script.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;~/myprojectdir/myproject/&lt;/code&gt;: The Django project package.  This should contain the &lt;code&gt;__init__.py&lt;/code&gt;, &lt;code&gt;settings.py&lt;/code&gt;, &lt;code&gt;urls.py&lt;/code&gt;, and &lt;code&gt;wsgi.py&lt;/code&gt; files.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;~/myprojectdir/myprojectenv/&lt;/code&gt;: The virtual environment directory we created earlier.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id="adjusting-the-project-settings"&gt;Adjusting the Project Settings&lt;/h3&gt;

&lt;p&gt;The first thing we should do with our newly created project files is adjust the settings.  Open the settings file in your text editor:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="(myprojectenv) $"&gt;nano ~/&lt;span class="highlight"&gt;myprojectdir&lt;/span&gt;/&lt;span class="highlight"&gt;myproject&lt;/span&gt;/settings.py
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Start by locating the &lt;code&gt;ALLOWED_HOSTS&lt;/code&gt; directive.  This defines a list of the server's addresses or domain names may be used to connect to the Django instance.  Any incoming requests with a &lt;strong&gt;Host&lt;/strong&gt; header that is not in this list will raise an exception.  Django requires that you set this to prevent a certain class of security vulnerability.&lt;/p&gt;

&lt;p&gt;In the square brackets, list the IP addresses or domain names that are associated with your Django server.  Each item should be listed in quotations with entries separated by a comma.  If you wish requests for an entire domain and any subdomains, prepend a period to the beginning of the entry.  In the snippet below, there are a few commented out examples used to demonstrate:&lt;/p&gt;

&lt;p&gt;&lt;span class='note'&gt;&lt;strong&gt;Note:&lt;/strong&gt; Be sure to include &lt;code&gt;localhost&lt;/code&gt; as one of the options since we will be proxying connections through a local Nginx instance.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;
&lt;div class="code-label " title="~/myprojectdir/myproject/settings.py"&gt;~/myprojectdir/myproject/settings.py&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .
# The simplest case: just add the domain name(s) and IP addresses of your Django server
# ALLOWED_HOSTS = [ 'example.com', '203.0.113.5']
# To respond to 'example.com' and any subdomains, start the domain with a dot
# ALLOWED_HOSTS = ['.example.com', '203.0.113.5']
ALLOWED_HOSTS = ['&lt;span class="highlight"&gt;your_server_domain_or_IP&lt;/span&gt;', '&lt;span class="highlight"&gt;second_domain_or_IP&lt;/span&gt;', &lt;span class="highlight"&gt;. . .&lt;/span&gt;, 'localhost']
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, find the section that configures database access.  It will start with &lt;code&gt;DATABASES&lt;/code&gt;.  The configuration in the file is for a SQLite database.  We already created a PostgreSQL database for our project, so we need to adjust the settings.&lt;/p&gt;

&lt;p&gt;Change the settings with your PostgreSQL database information.  We tell Django to use the &lt;code&gt;psycopg2&lt;/code&gt; adaptor we installed with &lt;code&gt;pip&lt;/code&gt;.  We need to give the database name, the database username, the database user's password, and then specify that the database is located on the local computer.  You can leave the &lt;code&gt;PORT&lt;/code&gt; setting as an empty string:&lt;/p&gt;
&lt;div class="code-label " title="~/myprojectdir/myproject/settings.py"&gt;~/myprojectdir/myproject/settings.py&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.&lt;span class="highlight"&gt;postgresql_psycopg2&lt;/span&gt;',
        'NAME': '&lt;span class="highlight"&gt;myproject&lt;/span&gt;',
        'USER': '&lt;span class="highlight"&gt;myprojectuser&lt;/span&gt;',
        'PASSWORD': '&lt;span class="highlight"&gt;password&lt;/span&gt;',
        'HOST': 'localhost',
        'PORT': '',
    }
}

. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, move down to the bottom of the file and add a setting indicating where the static files should be placed.  This is necessary so that Nginx can handle requests for these items.  The following line tells Django to place them in a directory called &lt;code&gt;static&lt;/code&gt; in the base project directory:&lt;/p&gt;
&lt;div class="code-label " title="~/myprojectdir/myproject/settings.py"&gt;~/myprojectdir/myproject/settings.py&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .

STATIC_URL = '/static/'
&lt;span class="highlight"&gt;STATIC_ROOT = os.path.join(BASE_DIR, 'static/')&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save and close the file when you are finished.&lt;/p&gt;

&lt;h3 id="completing-initial-project-setup"&gt;Completing Initial Project Setup&lt;/h3&gt;

&lt;p&gt;Now, we can migrate the initial database schema to our PostgreSQL database using the management script:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="(myprojectenv) $"&gt;~/&lt;span class="highlight"&gt;myprojectdir&lt;/span&gt;/manage.py makemigrations
&lt;/li&gt;&lt;li class="line" prefix="(myprojectenv) $"&gt;~/&lt;span class="highlight"&gt;myprojectdir&lt;/span&gt;/manage.py migrate
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Create an administrative user for the project by typing:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="(myprojectenv) $"&gt;~/&lt;span class="highlight"&gt;myprojectdir&lt;/span&gt;/manage.py createsuperuser
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will have to select a username, provide an email address, and choose and confirm a password.&lt;/p&gt;

&lt;p&gt;We can collect all of the static content into the directory location we configured by typing:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="(myprojectenv) $"&gt;~/&lt;span class="highlight"&gt;myprojectdir&lt;/span&gt;/manage.py collectstatic
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will have to confirm the operation.  The static files will then be placed in a directory called &lt;code&gt;static&lt;/code&gt; within your project directory.&lt;/p&gt;

&lt;p&gt;If you followed the initial server setup guide, you should have a UFW firewall protecting your server.  In order to test the development server, we'll have to allow access to the port we'll be using.&lt;/p&gt;

&lt;p&gt;Create an exception for port 8000 by typing:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="(myprojectenv) $"&gt;sudo ufw allow 8000
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, you can test our your project by starting up the Django development server with this command:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="(myprojectenv) $"&gt;~/&lt;span class="highlight"&gt;myprojectdir&lt;/span&gt;/manage.py runserver 0.0.0.0:8000
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In your web browser, visit your server's domain name or IP address followed by &lt;code&gt;:8000&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;http://&lt;span class="highlight"&gt;server_domain_or_IP&lt;/span&gt;:8000
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see the default Django index page:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/django_gunicorn_nginx_1804/django_index.png" alt="Django index page"&gt;&lt;/p&gt;

&lt;p&gt;If you append &lt;code&gt;/admin&lt;/code&gt; to the end of the URL in the address bar, you will be prompted for the administrative username and password you created with the &lt;code&gt;createsuperuser&lt;/code&gt; command:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/django_gunicorn_nginx_1804/admin_login.png" alt="Django admin login"&gt;&lt;/p&gt;

&lt;p&gt;After authenticating, you can access the default Django admin interface:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/django_gunicorn_nginx_1804/admin_interface.png" alt="Django admin interface"&gt;&lt;/p&gt;

&lt;p&gt;When you are finished exploring, hit &lt;strong&gt;CTRL-C&lt;/strong&gt; in the terminal window to shut down the development server.&lt;/p&gt;

&lt;h3 id="testing-gunicorn-39-s-ability-to-serve-the-project"&gt;Testing Gunicorn's Ability to Serve the Project&lt;/h3&gt;

&lt;p&gt;The last thing we want to do before leaving our virtual environment is test Gunicorn to make sure that it can serve the application.  We can do this by entering our project directory and using &lt;code&gt;gunicorn&lt;/code&gt; to load the project's WSGI module:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="(myprojectenv) $"&gt;cd ~/&lt;span class="highlight"&gt;myprojectdir&lt;/span&gt;
&lt;/li&gt;&lt;li class="line" prefix="(myprojectenv) $"&gt;gunicorn --bind 0.0.0.0:8000 &lt;span class="highlight"&gt;myproject&lt;/span&gt;.wsgi
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will start Gunicorn on the same interface that the Django development server was running on.  You can go back and test the app again.&lt;/p&gt;

&lt;p&gt;&lt;span class='note'&gt;&lt;strong&gt;Note:&lt;/strong&gt; The admin interface will not have any of the styling applied since Gunicorn does not know how to find the static CSS content responsible for this.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;We passed Gunicorn a module by specifying the relative directory path to Django's &lt;code&gt;wsgi.py&lt;/code&gt; file, which is the entry point to our application, using Python's module syntax.  Inside of this file, a function called &lt;code&gt;application&lt;/code&gt; is defined, which is used to communicate with the application.  To learn more about the WSGI specification, click &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-set-up-uwsgi-and-nginx-to-serve-python-apps-on-ubuntu-14-04#definitions-and-concepts"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;When you are finished testing, hit &lt;strong&gt;CTRL-C&lt;/strong&gt; in the terminal window to stop Gunicorn.&lt;/p&gt;

&lt;p&gt;We're now finished configuring our Django application.  We can back out of our virtual environment by typing:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="(myprojectenv) $"&gt;deactivate
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The virtual environment indicator in your prompt will be removed.&lt;/p&gt;

&lt;h2 id="step-5-—-creating-systemd-socket-and-service-files-for-gunicorn"&gt;Step 5 — Creating systemd Socket and Service Files for Gunicorn&lt;/h2&gt;

&lt;p&gt;We have tested that Gunicorn can interact with our Django application, but we should implement a more robust way of starting and stopping the application server. To accomplish this, we'll make systemd service and socket files.&lt;/p&gt;

&lt;p&gt;The Gunicorn socket will be created at boot and will listen for connections.  When a connection occurs, systemd will automatically start the Gunicorn process to handle the connection.&lt;/p&gt;

&lt;p&gt;Start by creating and opening a systemd socket file for Gunicorn with &lt;code&gt;sudo&lt;/code&gt; privileges:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/systemd/system/gunicorn.socket
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Inside, we will create a &lt;code&gt;[Unit]&lt;/code&gt; section to describe the socket, a &lt;code&gt;[Socket]&lt;/code&gt; section to define the socket location, and an &lt;code&gt;[Install]&lt;/code&gt; section to make sure the socket is created at the right time:&lt;/p&gt;
&lt;div class="code-label " title="/etc/systemd/system/gunicorn.socket"&gt;/etc/systemd/system/gunicorn.socket&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;[Unit]
Description=gunicorn socket

[Socket]
ListenStream=/run/gunicorn.sock

[Install]
WantedBy=sockets.target
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save and close the file when you are finished.&lt;/p&gt;

&lt;p&gt;Next, create and open a systemd service file for Gunicorn with &lt;code&gt;sudo&lt;/code&gt; privileges in your text editor.  The service filename should match the socket filename with the exception of the extension:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/systemd/system/gunicorn.service
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Start with the &lt;code&gt;[Unit]&lt;/code&gt; section, which is used to specify metadata and dependencies.  We'll put a description of our service here and tell the init system to only start this after the networking target has been reached.  Because our service relies on the socket from the socket file, we need to include a &lt;code&gt;Requires&lt;/code&gt; directive to indicate that relationship:&lt;/p&gt;
&lt;div class="code-label " title="/etc/systemd/system/gunicorn.service"&gt;/etc/systemd/system/gunicorn.service&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;[Unit]
Description=gunicorn daemon
Requires=gunicorn.socket
After=network.target
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, we'll open up the &lt;code&gt;[Service]&lt;/code&gt; section. We'll specify the user and group that we want to process to run under. We will give our regular user account ownership of the process since it owns all of the relevant files. We'll give group ownership to the &lt;code&gt;www-data&lt;/code&gt; group so that Nginx can communicate easily with Gunicorn.&lt;/p&gt;

&lt;p&gt;We'll then map out the working directory and specify the command to use to start the service.  In this case, we'll have to specify the full path to the Gunicorn executable, which is installed within our virtual environment.  We will bind the process to the Unix socket we created within the &lt;code&gt;/run&lt;/code&gt; directory so that the process can communicate with Nginx.  We log all data to standard output so that the &lt;code&gt;journald&lt;/code&gt; process can collect the Gunicorn logs.  We can also specify any optional Gunicorn tweaks here.  For example, we specified 3 worker processes in this case:&lt;/p&gt;
&lt;div class="code-label " title="/etc/systemd/system/gunicorn.service"&gt;/etc/systemd/system/gunicorn.service&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;[Unit]
Description=gunicorn daemon
Requires=gunicorn.socket
After=network.target

[Service]
User=&lt;span class="highlight"&gt;sammy&lt;/span&gt;
Group=www-data
WorkingDirectory=/home/&lt;span class="highlight"&gt;sammy&lt;/span&gt;/&lt;span class="highlight"&gt;myprojectdir&lt;/span&gt;
ExecStart=/home/&lt;span class="highlight"&gt;sammy&lt;/span&gt;/&lt;span class="highlight"&gt;myprojectdir&lt;/span&gt;/&lt;span class="highlight"&gt;myprojectenv&lt;/span&gt;/bin/gunicorn \
          --access-logfile - \
          --workers 3 \
          --bind unix:/run/gunicorn.sock \
          &lt;span class="highlight"&gt;myproject&lt;/span&gt;.wsgi:application
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, we'll add an &lt;code&gt;[Install]&lt;/code&gt; section. This will tell systemd what to link this service to if we enable it to start at boot. We want this service to start when the regular multi-user system is up and running:&lt;/p&gt;
&lt;div class="code-label " title="/etc/systemd/system/gunicorn.service"&gt;/etc/systemd/system/gunicorn.service&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;[Unit]
Description=gunicorn daemon
Requires=gunicorn.socket
After=network.target

[Service]
User=&lt;span class="highlight"&gt;sammy&lt;/span&gt;
Group=www-data
WorkingDirectory=/home/&lt;span class="highlight"&gt;sammy&lt;/span&gt;/&lt;span class="highlight"&gt;myprojectdir&lt;/span&gt;
ExecStart=/home/&lt;span class="highlight"&gt;sammy&lt;/span&gt;/&lt;span class="highlight"&gt;myprojectdir&lt;/span&gt;/&lt;span class="highlight"&gt;myprojectenv&lt;/span&gt;/bin/gunicorn \
          --access-logfile - \
          --workers 3 \
          --bind unix:/run/gunicorn.sock \
          &lt;span class="highlight"&gt;myproject&lt;/span&gt;.wsgi:application

[Install]
WantedBy=multi-user.target
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With that, our systemd service file is complete. Save and close it now.&lt;/p&gt;

&lt;p&gt;We can now start and enable the Gunicorn socket.  This will create the socket file at &lt;code&gt;/run/gunicorn.sock&lt;/code&gt; now and at boot.  When a connection is made to that socket, systemd will automatically start the &lt;code&gt;gunicorn.service&lt;/code&gt; to handle it:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl start gunicorn.socket
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl enable gunicorn.socket
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can confirm that the operation was successful by checking for the socket file.&lt;/p&gt;

&lt;h2 id="step-6-—-checking-for-the-gunicorn-socket-file"&gt;Step 6 — Checking for the Gunicorn Socket File&lt;/h2&gt;

&lt;p&gt;Check the status of the process to find out whether it was able to start:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl status gunicorn.socket
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, check for the existence of the &lt;code&gt;gunicorn.sock&lt;/code&gt; file within the &lt;code&gt;/run&lt;/code&gt; directory:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;file /run/gunicorn.sock
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;/run/gunicorn.sock: socket
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If the &lt;code&gt;systemctl status&lt;/code&gt; command indicated that an error occurred or if you do not find the &lt;code&gt;gunicorn.sock&lt;/code&gt; file in the directory, it's an indication that the Gunicorn socket was not able to be created correctly.  Check the Gunicorn socket's logs by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo journalctl -u gunicorn.socket
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Take another look at your &lt;code&gt;/etc/systemd/system/gunicorn.socket&lt;/code&gt; file to fix any problems before continuing.&lt;/p&gt;

&lt;h2 id="step-7-—-testing-socket-activation"&gt;Step 7 — Testing Socket Activation&lt;/h2&gt;

&lt;p&gt;Currently, if you've only started the &lt;code&gt;gunicorn.socket&lt;/code&gt; unit, the &lt;code&gt;gunicorn.service&lt;/code&gt; will not be active yet since the socket has not yet received any connections.  You can check this by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl status gunicorn
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;● gunicorn.service - gunicorn daemon
   Loaded: loaded (/etc/systemd/system/gunicorn.service; disabled; vendor preset: enabled)
   &lt;span class="highlight"&gt;Active: inactive (dead)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To test the socket activation mechanism, we can send a connection to the socket through &lt;code&gt;curl&lt;/code&gt; by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;curl --unix-socket /run/gunicorn.sock localhost
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see the HTML output from your application in the terminal.  This indicates that Gunicorn was started and was able to serve your Django application.  You can verify that the Gunicorn service is running by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl status gunicorn
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;● gunicorn.service - gunicorn daemon
   Loaded: loaded (/etc/systemd/system/gunicorn.service; disabled; vendor preset: enabled)
   &lt;span class="highlight"&gt;Active: active (running)&lt;/span&gt; since Mon 2018-07-09 20:00:40 UTC; 4s ago
 Main PID: 1157 (gunicorn)
    Tasks: 4 (limit: 1153)
   CGroup: /system.slice/gunicorn.service
           ├─1157 /home/sammy/myprojectdir/myprojectenv/bin/python3 /home/sammy/myprojectdir/myprojectenv/bin/gunicorn --access-logfile - --workers 3 --bind unix:/run/gunicorn.sock myproject.wsgi:application
           ├─1178 /home/sammy/myprojectdir/myprojectenv/bin/python3 /home/sammy/myprojectdir/myprojectenv/bin/gunicorn --access-logfile - --workers 3 --bind unix:/run/gunicorn.sock myproject.wsgi:application
           ├─1180 /home/sammy/myprojectdir/myprojectenv/bin/python3 /home/sammy/myprojectdir/myprojectenv/bin/gunicorn --access-logfile - --workers 3 --bind unix:/run/gunicorn.sock myproject.wsgi:application
           └─1181 /home/sammy/myprojectdir/myprojectenv/bin/python3 /home/sammy/myprojectdir/myprojectenv/bin/gunicorn --access-logfile - --workers 3 --bind unix:/run/gunicorn.sock myproject.wsgi:application

Jul 09 20:00:40 django1 systemd[1]: Started gunicorn daemon.
Jul 09 20:00:40 django1 gunicorn[1157]: [2018-07-09 20:00:40 +0000] [1157] [INFO] Starting gunicorn 19.9.0
Jul 09 20:00:40 django1 gunicorn[1157]: [2018-07-09 20:00:40 +0000] [1157] [INFO] Listening at: unix:/run/gunicorn.sock (1157)
Jul 09 20:00:40 django1 gunicorn[1157]: [2018-07-09 20:00:40 +0000] [1157] [INFO] Using worker: sync
Jul 09 20:00:40 django1 gunicorn[1157]: [2018-07-09 20:00:40 +0000] [1178] [INFO] Booting worker with pid: 1178
Jul 09 20:00:40 django1 gunicorn[1157]: [2018-07-09 20:00:40 +0000] [1180] [INFO] Booting worker with pid: 1180
Jul 09 20:00:40 django1 gunicorn[1157]: [2018-07-09 20:00:40 +0000] [1181] [INFO] Booting worker with pid: 1181
Jul 09 20:00:41 django1 gunicorn[1157]:  - - [09/Jul/2018:20:00:41 +0000] "GET / HTTP/1.1" 200 16348 "-" "curl/7.58.0"
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If the output from &lt;code&gt;curl&lt;/code&gt; or the output of &lt;code&gt;systemctl status&lt;/code&gt; indicates that a problem occurred, check the logs for additional details:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo journalctl -u gunicorn
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Check your &lt;code&gt;/etc/systemd/system/gunicorn.service&lt;/code&gt; file for problems.  If you make changes to the &lt;code&gt;/etc/systemd/system/gunicorn.service&lt;/code&gt; file, reload the daemon to reread the service definition and restart the Gunicorn process by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl daemon-reload
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl restart gunicorn
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Make sure you troubleshoot the above issues before continuing.&lt;/p&gt;

&lt;h2 id="step-8-—-configure-nginx-to-proxy-pass-to-gunicorn"&gt;Step 8 — Configure Nginx to Proxy Pass to Gunicorn&lt;/h2&gt;

&lt;p&gt;Now that Gunicorn is set up, we need to configure Nginx to pass traffic to the process.&lt;/p&gt;

&lt;p&gt;Start by creating and opening a new server block in Nginx's &lt;code&gt;sites-available&lt;/code&gt; directory:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/nginx/sites-available/&lt;span class="highlight"&gt;myproject&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Inside, open up a new server block.  We will start by specifying that this block should listen on the normal port 80 and that it should respond to our server's domain name or IP address:&lt;/p&gt;
&lt;div class="code-label " title="/etc/nginx/sites-available/myproject"&gt;/etc/nginx/sites-available/myproject&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;server {
    listen 80;
    server_name &lt;span class="highlight"&gt;server_domain_or_IP&lt;/span&gt;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, we will tell Nginx to ignore any problems with finding a favicon.  We will also tell it where to find the static assets that we collected in our &lt;code&gt;~/&lt;span class="highlight"&gt;myprojectdir&lt;/span&gt;/static&lt;/code&gt; directory.  All of these files have a standard URI prefix of "/static", so we can create a location block to match those requests:&lt;/p&gt;
&lt;div class="code-label " title="/etc/nginx/sites-available/myproject"&gt;/etc/nginx/sites-available/myproject&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;server {
    listen 80;
    server_name &lt;span class="highlight"&gt;server_domain_or_IP&lt;/span&gt;;

    location = /favicon.ico { access_log off; log_not_found off; }
    location /static/ {
        root /home/&lt;span class="highlight"&gt;sammy&lt;/span&gt;/&lt;span class="highlight"&gt;myprojectdir&lt;/span&gt;;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, we'll create a &lt;code&gt;location / {}&lt;/code&gt; block to match all other requests.  Inside of this location, we'll include the standard &lt;code&gt;proxy_params&lt;/code&gt; file included with the Nginx installation and then we will pass the traffic directly to the Gunicorn socket:&lt;/p&gt;
&lt;div class="code-label " title="/etc/nginx/sites-available/myproject"&gt;/etc/nginx/sites-available/myproject&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;server {
    listen 80;
    server_name &lt;span class="highlight"&gt;server_domain_or_IP&lt;/span&gt;;

    location = /favicon.ico { access_log off; log_not_found off; }
    location /static/ {
        root /home/&lt;span class="highlight"&gt;sammy&lt;/span&gt;/&lt;span class="highlight"&gt;myprojectdir&lt;/span&gt;;
    }

    location / {
        include proxy_params;
        proxy_pass http://unix:/run/gunicorn.sock;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save and close the file when you are finished.  Now, we can enable the file by linking it to the &lt;code&gt;sites-enabled&lt;/code&gt; directory:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ln -s /etc/nginx/sites-available/&lt;span class="highlight"&gt;myproject&lt;/span&gt; /etc/nginx/sites-enabled
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Test your Nginx configuration for syntax errors by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nginx -t
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If no errors are reported, go ahead and restart Nginx by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl restart nginx
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, we need to open up our firewall to normal traffic on port 80.  Since we no longer need access to the development server, we can remove the rule to open port 8000 as well:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw delete allow 8000
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo ufw allow 'Nginx Full'
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should now be able to go to your server's domain or IP address to view your application.&lt;/p&gt;

&lt;span class='note'&gt;&lt;p&gt;
&lt;strong&gt;Note:&lt;/strong&gt; After configuring Nginx, the next step should be securing traffic to the server using SSL/TLS.  This is important because without it, all information, including passwords are sent over the network in plain text.&lt;/p&gt;

&lt;p&gt;If you have a domain name, the easiest way get an SSL certificate to secure your traffic is using Let's Encrypt.  Follow &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-secure-nginx-with-let-s-encrypt-on-debian-9"&gt;this guide&lt;/a&gt; to set up Let's Encrypt with Nginx on Debian 9.  Follow the procedure using the Nginx server block we created in this guide.&lt;/p&gt;

&lt;p&gt;If you do not have a domain name, you can still secure your site for testing and learning with a &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-create-a-self-signed-ssl-certificate-for-nginx-on-debian-9"&gt;self-signed SSL certificate&lt;/a&gt;.  Again, follow the process using the Nginx server block we created in this tutorial.&lt;br&gt;&lt;/p&gt;&lt;/span&gt;

&lt;h2 id="troubleshooting-nginx-and-gunicorn"&gt;Troubleshooting Nginx and Gunicorn&lt;/h2&gt;

&lt;p&gt;If this last step does not show your application, you will need to troubleshoot your installation.&lt;/p&gt;

&lt;h3 id="nginx-is-showing-the-default-page-instead-of-the-django-application"&gt;Nginx Is Showing the Default Page Instead of the Django Application&lt;/h3&gt;

&lt;p&gt;If Nginx displays the default page instead of proxying to your application, it usually means that you need to adjust the &lt;code&gt;server_name&lt;/code&gt; within the &lt;code&gt;/etc/nginx/sites-available/&lt;span class="highlight"&gt;myproject&lt;/span&gt;&lt;/code&gt; file to point to your server's IP address or domain name.&lt;/p&gt;

&lt;p&gt;Nginx uses the &lt;code&gt;server_name&lt;/code&gt; to determine which server block to use to respond to requests.  If you are seeing the default Nginx page, it is a sign that Nginx wasn't able to match the request to a sever block explicitly, so it's falling back on the default block defined in &lt;code&gt;/etc/nginx/sites-available/default&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;server_name&lt;/code&gt; in your project's server block must be more specific than the one in the default server block to be selected.&lt;/p&gt;

&lt;h3 id="nginx-is-displaying-a-502-bad-gateway-error-instead-of-the-django-application"&gt;Nginx Is Displaying a 502 Bad Gateway Error Instead of the Django Application&lt;/h3&gt;

&lt;p&gt;A 502 error indicates that Nginx is unable to successfully proxy the request.  A wide range of configuration problems express themselves with a 502 error, so more information is required to troubleshoot properly.&lt;/p&gt;

&lt;p&gt;The primary place to look for more information is in Nginx's error logs.  Generally, this will tell you what conditions caused problems during the proxying event.  Follow the Nginx error logs by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo tail -F /var/log/nginx/error.log
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, make another request in your browser to generate a fresh error (try refreshing the page).  You should see a fresh error message written to the log.  If you look at the message, it should help you narrow down the problem.&lt;/p&gt;

&lt;p&gt;You might see some of the following message:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;connect() to unix:/run/gunicorn.sock failed (2: No such file or directory)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This indicates that Nginx was unable to find the &lt;code&gt;gunicorn.sock&lt;/code&gt; file at the given location.  You should compare the &lt;code&gt;proxy_pass&lt;/code&gt; location defined within &lt;code&gt;/etc/nginx/sites-available/myproject&lt;/code&gt; file to the actual location of the &lt;code&gt;gunicorn.sock&lt;/code&gt; file generated by the &lt;code&gt;gunicorn.socket&lt;/code&gt; systemd unit.&lt;/p&gt;

&lt;p&gt;If you cannot find a &lt;code&gt;gunicorn.sock&lt;/code&gt; file within the &lt;code&gt;/run&lt;/code&gt; directory, it generally means that the systemd socket file was unable to create it.  Go back to the &lt;a href="#checking-for-the-gunicorn-socket-file"&gt;section on checking for the Gunicorn socket file&lt;/a&gt; to step through the troubleshooting steps for Gunicorn.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;connect() to unix:/run/gunicorn.sock failed (13: Permission denied)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This indicates that Nginx was unable to connect to the Gunicorn socket because of permissions problems.  This can happen when the procedure is followed using the root user instead of a &lt;code&gt;sudo&lt;/code&gt; user.  While systemd is able to create the Gunicorn socket file, Nginx is unable to access it.&lt;/p&gt;

&lt;p&gt;This can happen if there are limited permissions at any point between the root directory (&lt;code&gt;/&lt;/code&gt;) the &lt;code&gt;gunicorn.sock&lt;/code&gt; file.  We can see the permissions and ownership values of the socket file and each of its parent directories by passing the absolute path to our socket file to the &lt;code&gt;namei&lt;/code&gt; command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;namei -l /run/gunicorn.sock
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;f: /run/gunicorn.sock
drwxr-xr-x root root /
drwxr-xr-x root root run
srw-rw-rw- root root gunicorn.sock
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The output displays the permissions of each of the directory components.  By looking at the permissions (first column), owner (second column) and group owner (third column), we can figure out what type of access is allowed to the socket file.&lt;/p&gt;

&lt;p&gt;In the above example, the socket file and each of the directories leading up to the socket file have world read and execute permissions (the permissions column for the directories end with &lt;code&gt;r-x&lt;/code&gt; instead of &lt;code&gt;---&lt;/code&gt;).  The Nginx process should be able to access the socket successfully.&lt;/p&gt;

&lt;p&gt;If any of the directories leading up to the socket do not have world read and execute permission, Nginx will not be able to access the socket without allowing world read and execute permissions or making sure group ownership is given to a group that Nginx is a part of.&lt;/p&gt;

&lt;h3 id="django-is-displaying-quot-could-not-connect-to-server-connection-refused-quot"&gt;Django Is Displaying: "could not connect to server: Connection refused"&lt;/h3&gt;

&lt;p&gt;One message that you may see from Django when attempting to access parts of the application in the web browser is:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;OperationalError at /admin/login/
could not connect to server: Connection refused
    Is the server running on host "localhost" (127.0.0.1) and accepting
    TCP/IP connections on port 5432?
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This indicates that Django is unable to connect to the Postgres database.  Make sure that the Postgres instance is running by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl status postgresql
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If it is not, you can start it and enable it to start automatically at boot (if it is not already configured to do so) by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl start postgresql
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl enable postgresql
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you are still having issues, make sure the database settings defined in the &lt;code&gt;~/myprojectdir/myproject/settings.py&lt;/code&gt; file are correct.&lt;/p&gt;

&lt;h3 id="further-troubleshooting"&gt;Further Troubleshooting&lt;/h3&gt;

&lt;p&gt;For additional troubleshooting, the logs can help narrow down root causes.  Check each of them in turn and look for messages indicating problem areas.&lt;/p&gt;

&lt;p&gt;The following logs may be helpful:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check the Nginx process logs by typing: &lt;code&gt;sudo journalctl -u nginx&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Check the Nginx access logs by typing: &lt;code&gt;sudo less /var/log/nginx/access.log&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Check the Nginx error logs by typing: &lt;code&gt;sudo less /var/log/nginx/error.log&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Check the Gunicorn application logs by typing: &lt;code&gt;sudo journalctl -u gunicorn&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Check the Gunicorn socket logs by typing: &lt;code&gt;sudo journalctl -u gunicorn.socket&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As you update your configuration or application, you will likely need to restart the processes to adjust to your changes.&lt;/p&gt;

&lt;p&gt;If you update your Django application, you can restart the Gunicorn process to pick up the changes by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl restart gunicorn
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you change Gunicorn socket or service files, reload the daemon and restart the process by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl daemon-reload
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl restart gunicorn.socket gunicorn.service
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you change the Nginx server block configuration, test the configuration and then Nginx by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nginx -t &amp;amp;&amp;amp; sudo systemctl restart nginx
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These commands are helpful for picking up changes as you adjust your configuration.&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;In this guide, we've set up a Django project in its own virtual environment.  We've configured Gunicorn to translate client requests so that Django can handle them.  Afterwards, we set up Nginx to act as a reverse proxy to handle client connections and serve the correct project depending on the client request.&lt;/p&gt;

&lt;p&gt;Django makes creating projects and applications simple by providing many of the common pieces, allowing you to focus on the unique elements.  By leveraging the general tool chain described in this article, you can easily serve the applications you create from a single server.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-configure-bind-as-a-private-network-dns-server-on-debian-9</id>
    <published>2018-09-06T19:18:17Z</published>
    <updated>2018-09-06T19:18:40Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-configure-bind-as-a-private-network-dns-server-on-debian-9"/>
    <title>How To Configure BIND as a Private Network DNS Server on Debian 9</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;An important part of managing server configuration and infrastructure includes maintaining an easy way to look up network interfaces and IP addresses by name, by setting up a proper Domain Name System (DNS).  Using fully qualified domain names (FQDNs), instead of IP addresses, to specify network addresses eases the configuration of services and applications, and increases the maintainability of configuration files.  Setting up your own DNS for your private network is a great way to improve the management of your servers.&lt;/p&gt;

&lt;p&gt;In this tutorial, we will go over how to set up an internal DNS server, using the BIND name server software (BIND9) on Debian 9, that can be used by your servers to resolve private hostnames and private IP addresses.  This provides a central way to manage your internal hostnames and private IP addresses, which is indispensable when your environment expands to more than a few hosts.&lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;To complete this tutorial, you will need the following infrastructure.  Create each server &lt;strong&gt;in the same datacenter&lt;/strong&gt; with &lt;strong&gt;&lt;a href="https://www.digitalocean.com/docs/networking/private-networking/quickstart/"&gt;private networking enabled&lt;/a&gt;&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A fresh Debian 9 server to serve as the Primary DNS server, &lt;strong&gt;ns1&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;(Recommended) A second Debian 9 server to serve as a Secondary DNS server, &lt;strong&gt;ns2&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Additional servers in the same datacenter that will be using your DNS servers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On each of these servers, configure administrative access via a &lt;code&gt;sudo&lt;/code&gt; user and a firewall by following our &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-debian-9"&gt;Debian 9 initial server setup guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you are unfamiliar with DNS concepts, it is recommended that you read at least the first three parts of our &lt;a href="https://www.digitalocean.com/community/tutorial_series/an-introduction-to-managing-dns"&gt;Introduction to Managing DNS&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id="example-infrastructure-and-goals"&gt;Example Infrastructure and Goals&lt;/h3&gt;

&lt;p&gt;For the purposes of this article, we will assume the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We have two servers which will be designated as our DNS name servers.  We will refer to these as &lt;strong&gt;ns1&lt;/strong&gt; and &lt;strong&gt;ns2&lt;/strong&gt; in this guide.&lt;/li&gt;
&lt;li&gt;We have two additional client servers that will be using the DNS infrastructure we create.  We will call these &lt;strong&gt;host1&lt;/strong&gt; and &lt;strong&gt;host2&lt;/strong&gt; in this guide.  You can add as many as you'd like for your infrastructure.&lt;/li&gt;
&lt;li&gt;All of these servers exist in the same datacenter.  We will assume that this is the &lt;strong&gt;nyc3&lt;/strong&gt; datacenter.&lt;/li&gt;
&lt;li&gt;All of these servers have private networking enabled (and are on the &lt;code&gt;10.128.0.0/16&lt;/code&gt; subnet.  You will likely have to adjust this for your servers).&lt;/li&gt;
&lt;li&gt;All servers are connected to a project that runs on "example.com".  Since our DNS system will be entirely internal and private, you do not have to purchase a domain name.  However, using a domain you own may help avoid conflicts with publicly routable domains.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With these assumptions, we decide that it makes sense to use a naming scheme that uses "nyc3.example.com" to refer to our private subnet or zone.  Therefore, &lt;strong&gt;host1&lt;/strong&gt;'s private Fully-Qualified Domain Name (FQDN) will be &lt;strong&gt;host1.nyc3.example.com&lt;/strong&gt;.  Refer to the following table the relevant details:&lt;/p&gt;

&lt;table&gt;&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Host&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;Private FQDN&lt;/th&gt;
&lt;th&gt;Private IP Address&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ns1&lt;/td&gt;
&lt;td&gt;Primary DNS Server&lt;/td&gt;
&lt;td&gt;ns1.nyc3.example.com&lt;/td&gt;
&lt;td&gt;10.128.10.11&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ns2&lt;/td&gt;
&lt;td&gt;Secondary DNS Server&lt;/td&gt;
&lt;td&gt;ns2.nyc3.example.com&lt;/td&gt;
&lt;td&gt;10.128.20.12&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;host1&lt;/td&gt;
&lt;td&gt;Generic Host 1&lt;/td&gt;
&lt;td&gt;host1.nyc3.example.com&lt;/td&gt;
&lt;td&gt;10.128.100.101&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;host2&lt;/td&gt;
&lt;td&gt;Generic Host 2&lt;/td&gt;
&lt;td&gt;host2.nyc3.example.com&lt;/td&gt;
&lt;td&gt;10.128.200.102&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;

&lt;p&gt;&lt;div class='code-label notes-and-warnings note' title='Note'&gt;Note&lt;/div&gt;&lt;span class='note'&gt;
Your existing setup will be different, but the example names and IP addresses will be used to demonstrate how to configure a DNS server to provide a functioning internal DNS.  You should be able to easily adapt this setup to your own environment by replacing the host names and private IP addresses with your own.  It is not necessary to use the region name of the datacenter in your naming scheme, but we use it here to denote that these hosts belong to a particular datacenter's private network.  If you utilize multiple datacenters, you can set up an internal DNS within each respective datacenter.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;By the end of this tutorial, we will have a primary DNS server, &lt;strong&gt;ns1&lt;/strong&gt;, and optionally a secondary DNS server, &lt;strong&gt;ns2&lt;/strong&gt;, which will serve as a backup.&lt;/p&gt;

&lt;p&gt;Let's get started by installing our Primary DNS server, ns1.&lt;/p&gt;

&lt;h2 id="installing-bind-on-dns-servers"&gt;Installing BIND on DNS Servers&lt;/h2&gt;

&lt;p&gt;&lt;div class='code-label notes-and-warnings note' title='Note'&gt;Note&lt;/div&gt;&lt;span class='note'&gt;
Text that is highlighted in &lt;span class="highlight"&gt;red&lt;/span&gt; is important!  It will often be used to denote something that needs to be replaced with your own settings or that it should be modified or added to a configuration file.  For example, if you see something like &lt;code&gt;&lt;span class="highlight"&gt;host1.nyc3.example.com&lt;/span&gt;&lt;/code&gt;, replace it with the FQDN of your own server.  Likewise, if you see &lt;code&gt;&lt;span class="highlight"&gt;host1_private_IP&lt;/span&gt;&lt;/code&gt;, replace it with the private IP address of your own server.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;On both DNS servers, &lt;strong&gt;ns1&lt;/strong&gt; and &lt;strong&gt;ns2&lt;/strong&gt;, update the &lt;code&gt;apt&lt;/code&gt; package cache by typing:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="ns$"&gt;sudo apt update
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now install BIND:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="ns$"&gt;sudo apt install bind9 bind9utils bind9-doc
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="setting-bind-to-ipv4-mode"&gt;Setting Bind to IPv4 Mode&lt;/h3&gt;

&lt;p&gt;Before continuing, let's set BIND to IPv4 mode since our private networking uses IPv4 exclusively.  On both servers, edit the &lt;code&gt;bind9&lt;/code&gt; default settings file by typing:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="ns$"&gt;sudo nano /etc/default/bind9
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add "-4" to the end of the &lt;code&gt;OPTIONS&lt;/code&gt; parameter. It should look like the following:&lt;/p&gt;
&lt;div class="code-label " title="/etc/default/bind9"&gt;/etc/default/bind9&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .
OPTIONS="-u bind &lt;span class="highlight"&gt;-4&lt;/span&gt;"
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save and close the file when you are finished.&lt;/p&gt;

&lt;p&gt;Restart BIND to implement the changes:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="ns$"&gt;sudo systemctl restart bind9
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that BIND is installed, let's configure the primary DNS server.&lt;/p&gt;

&lt;h2 id="configuring-the-primary-dns-server"&gt;Configuring the Primary DNS Server&lt;/h2&gt;

&lt;p&gt;BIND's configuration consists of multiple files, which are included from the main configuration file, &lt;code&gt;named.conf&lt;/code&gt;.  These filenames begin with &lt;code&gt;named&lt;/code&gt; because that is the name of the process that BIND runs (short for "domain name daemon").  We will start with configuring the options file.&lt;/p&gt;

&lt;h3 id="configuring-the-options-file"&gt;Configuring the Options File&lt;/h3&gt;

&lt;p&gt;On &lt;strong&gt;ns1&lt;/strong&gt;, open the &lt;code&gt;named.conf.options&lt;/code&gt; file for editing:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix second-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="ns1$"&gt;sudo nano /etc/bind/named.conf.options
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Above the existing &lt;code&gt;options&lt;/code&gt; block, create a &lt;em&gt;new&lt;/em&gt; ACL (access control list) block called "trusted".  This is where we will define a list of clients that we will allow recursive DNS queries from (i.e. your servers that are in the same datacenter as &lt;strong&gt;ns1&lt;/strong&gt;).  Using our example private IP addresses, we will add &lt;strong&gt;ns1&lt;/strong&gt;, &lt;strong&gt;ns2&lt;/strong&gt;, &lt;strong&gt;host1&lt;/strong&gt;, and &lt;strong&gt;host2&lt;/strong&gt; to our list of trusted clients:&lt;/p&gt;
&lt;div class="code-label " title="/etc/bind/named.conf.options — 1 of 3"&gt;/etc/bind/named.conf.options — 1 of 3&lt;/div&gt;&lt;pre class="code-pre  second-environment"&gt;&lt;code langs=""&gt;acl "trusted" {
        &lt;span class="highlight"&gt;10.128.10.11&lt;/span&gt;;    # ns1 - can be set to localhost
        &lt;span class="highlight"&gt;10.128.20.12&lt;/span&gt;;    # ns2
        &lt;span class="highlight"&gt;10.128.100.101&lt;/span&gt;;  # host1
        &lt;span class="highlight"&gt;10.128.200.102&lt;/span&gt;;  # host2
};

options {

        . . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that we have our list of trusted DNS clients, we will want to edit the &lt;code&gt;options&lt;/code&gt; block.  Currently, the start of the block looks like the following:&lt;/p&gt;
&lt;div class="code-label " title="/etc/bind/named.conf.options — 2 of 3"&gt;/etc/bind/named.conf.options — 2 of 3&lt;/div&gt;&lt;pre class="code-pre  second-environment"&gt;&lt;code langs=""&gt;        . . .
};

options {
        directory "/var/cache/bind";
        . . .
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Below the &lt;code&gt;directory&lt;/code&gt; directive, add the highlighted configuration lines (and substitute in the proper &lt;strong&gt;ns1&lt;/strong&gt; IP address) so it looks something like this:&lt;/p&gt;
&lt;div class="code-label " title="/etc/bind/named.conf.options — 3 of 3"&gt;/etc/bind/named.conf.options — 3 of 3&lt;/div&gt;&lt;pre class="code-pre  second-environment"&gt;&lt;code langs=""&gt;        . . .

};

options {
        directory "/var/cache/bind";

        &lt;span class="highlight"&gt;recursion yes;&lt;/span&gt;                 # enables resursive queries
        &lt;span class="highlight"&gt;allow-recursion { trusted; };&lt;/span&gt;  # allows recursive queries from "trusted" clients
        &lt;span class="highlight"&gt;listen-on { 10.128.10.11; };&lt;/span&gt;   # ns1 private IP address - listen on private network only
        &lt;span class="highlight"&gt;allow-transfer { none; };&lt;/span&gt;      # disable zone transfers by default

        &lt;span class="highlight"&gt;forwarders {&lt;/span&gt;
                &lt;span class="highlight"&gt;8.8.8.8;&lt;/span&gt;
                &lt;span class="highlight"&gt;8.8.4.4;&lt;/span&gt;
        &lt;span class="highlight"&gt;};&lt;/span&gt;

        . . .
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When you are finished, save and close the &lt;code&gt;named.conf.options&lt;/code&gt; file.  The above configuration specifies that only your own servers (the "trusted" ones) will be able to query your DNS server for outside domains.&lt;/p&gt;

&lt;p&gt;Next, we will configure the local file, to specify our DNS zones.&lt;/p&gt;

&lt;h3 id="configuring-the-local-file"&gt;Configuring the Local File&lt;/h3&gt;

&lt;p&gt;On &lt;strong&gt;ns1&lt;/strong&gt;, open the &lt;code&gt;named.conf.local&lt;/code&gt; file for editing:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix second-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="ns1$"&gt;sudo nano /etc/bind/named.conf.local
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Aside from a few comments, the file should be empty. Here, we will specify our forward and reverse zones.  &lt;strong&gt;DNS zones&lt;/strong&gt; designate a specific scope for managing and defining DNS records.  Since our domains will all be within the "nyc3.example.com" subdomain, we will use that as our forward zone.  Because our servers' private IP addresses are each in the &lt;code&gt;10.128.0.0/16&lt;/code&gt; IP space, we will set up a reverse zone so that we can define reverse lookups within that range.&lt;/p&gt;

&lt;p&gt;Add the forward zone with the following lines, substituting the zone name with your own and the &lt;strong&gt;secondary DNS server's private IP address&lt;/strong&gt; in the &lt;code&gt;allow-transfer&lt;/code&gt; directive:&lt;/p&gt;
&lt;div class="code-label " title="/etc/bind/named.conf.local — 1 of 2"&gt;/etc/bind/named.conf.local — 1 of 2&lt;/div&gt;&lt;pre class="code-pre  second-environment"&gt;&lt;code langs=""&gt;zone "&lt;span class="highlight"&gt;nyc3.example.com&lt;/span&gt;" {
    type master;
    file "/etc/bind/zones/db.&lt;span class="highlight"&gt;nyc3.example.com&lt;/span&gt;"; # zone file path
    allow-transfer { &lt;span class="highlight"&gt;10.128.20.12&lt;/span&gt;; };           # ns2 private IP address - secondary
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Assuming that our private subnet is &lt;code&gt;10.128.0.0/16&lt;/code&gt;, add the reverse zone by with the following lines (&lt;strong&gt;note that our reverse zone name starts with "128.10" which is the octet reversal of "10.128"&lt;/strong&gt;):&lt;/p&gt;
&lt;div class="code-label " title="/etc/bind/named.conf.local — 2 of 2"&gt;/etc/bind/named.conf.local — 2 of 2&lt;/div&gt;&lt;pre class="code-pre  second-environment"&gt;&lt;code langs=""&gt;    . . .
};

zone "&lt;span class="highlight"&gt;128.10&lt;/span&gt;.in-addr.arpa" {
    type master;
    file "/etc/bind/zones/db.&lt;span class="highlight"&gt;10.128&lt;/span&gt;";  # 10.128.0.0/16 subnet
    allow-transfer { &lt;span class="highlight"&gt;10.128.20.12&lt;/span&gt;; };  # ns2 private IP address - secondary
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If your servers span multiple private subnets but are in the same datacenter, be sure to specify an additional zone and zone file for each distinct subnet.  When you are finished adding all of your desired zones, save and exit the &lt;code&gt;named.conf.local&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Now that our zones are specified in BIND, we need to create the corresponding forward and reverse zone files.&lt;/p&gt;

&lt;h3 id="creating-the-forward-zone-file"&gt;Creating the Forward Zone File&lt;/h3&gt;

&lt;p&gt;The forward zone file is where we define DNS records for forward DNS lookups.  That is, when the DNS receives a name query, "host1.nyc3.example.com" for example, it will look in the forward zone file to resolve &lt;strong&gt;host1&lt;/strong&gt;'s corresponding private IP address.&lt;/p&gt;

&lt;p&gt;Let's create the directory where our zone files will reside.  According to our &lt;strong&gt;named.conf.local&lt;/strong&gt; configuration, that location should be &lt;code&gt;/etc/bind/zones&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix second-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="ns1$"&gt;sudo mkdir /etc/bind/zones
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We will base our forward zone file on the sample &lt;code&gt;db.local&lt;/code&gt; zone file.  Copy it to the proper location with the following commands:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix second-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="ns1$"&gt;sudo cp /etc/bind/db.local /etc/bind/zones/db.&lt;span class="highlight"&gt;nyc3.example.com&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now let's edit our forward zone file:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix second-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="ns1$"&gt;sudo nano /etc/bind/zones/db.&lt;span class="highlight"&gt;nyc3.example.com&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Initially, it will look something like the following:&lt;/p&gt;
&lt;div class="code-label " title="/etc/bind/zones/db.nyc3.example.com — original"&gt;/etc/bind/zones/db.nyc3.example.com — original&lt;/div&gt;&lt;pre class="code-pre  second-environment"&gt;&lt;code langs=""&gt;$TTL    604800
@       IN      SOA     localhost. root.localhost. (
                              2         ; Serial
                         604800         ; Refresh
                          86400         ; Retry
                        2419200         ; Expire
                         604800 )       ; Negative Cache TTL
;
@       IN      NS      localhost.      ; delete this line
@       IN      A       127.0.0.1       ; delete this line
@       IN      AAAA    ::1             ; delete this line
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;First, you will want to edit the SOA record.  Replace the first "localhost" with &lt;strong&gt;ns1&lt;/strong&gt;'s FQDN, then replace "root.localhost" with "admin.nyc3.example.com".  Every time you edit a zone file, you need to increment the &lt;strong&gt;serial&lt;/strong&gt; value before you restart the &lt;code&gt;named&lt;/code&gt; process.  We will increment it to "3".  It should now look something like this:&lt;/p&gt;
&lt;div class="code-label " title="/etc/bind/zones/db.nyc3.example.com — updated 1 of 3"&gt;/etc/bind/zones/db.nyc3.example.com — updated 1 of 3&lt;/div&gt;&lt;pre class="code-pre  second-environment"&gt;&lt;code langs=""&gt;@       IN      SOA     &lt;span class="highlight"&gt;ns1.nyc3.example.com&lt;/span&gt;. &lt;span class="highlight"&gt;admin&lt;/span&gt;.&lt;span class="highlight"&gt;nyc3.example.com&lt;/span&gt;. (
                              &lt;span class="highlight"&gt;3&lt;/span&gt;         ; Serial

                              . . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, delete the three records at the end of the file (after the SOA record).  If you're not sure which lines to delete, they are marked with a "delete this line" comment above.&lt;/p&gt;

&lt;p&gt;At the end of the file, add your name server records with the following lines (replace the names with your own).  Note that the second column specifies that these are "NS" records:&lt;/p&gt;
&lt;div class="code-label " title="/etc/bind/zones/db.nyc3.example.com — updated 2 of 3"&gt;/etc/bind/zones/db.nyc3.example.com — updated 2 of 3&lt;/div&gt;&lt;pre class="code-pre  second-environment"&gt;&lt;code langs=""&gt;. . .

; name servers - NS records
    IN      NS      ns1.&lt;span class="highlight"&gt;nyc3.example.com&lt;/span&gt;.
    IN      NS      ns2.&lt;span class="highlight"&gt;nyc3.example.com&lt;/span&gt;.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, add the A records for your hosts that belong in this zone.  This includes any server whose name we want to end with ".nyc3.example.com" (substitute the names and private IP addresses).  Using our example names and private IP addresses, we will add A records for &lt;strong&gt;ns1&lt;/strong&gt;, &lt;strong&gt;ns2&lt;/strong&gt;, &lt;strong&gt;host1&lt;/strong&gt;, and &lt;strong&gt;host2&lt;/strong&gt; like so:&lt;/p&gt;
&lt;div class="code-label " title="/etc/bind/zones/db.nyc3.example.com — updated 3 of 3"&gt;/etc/bind/zones/db.nyc3.example.com — updated 3 of 3&lt;/div&gt;&lt;pre class="code-pre  second-environment"&gt;&lt;code langs=""&gt;. . .

; name servers - A records
ns1.&lt;span class="highlight"&gt;nyc3.example.com&lt;/span&gt;.          IN      A       &lt;span class="highlight"&gt;10.128.10.11&lt;/span&gt;
ns2.&lt;span class="highlight"&gt;nyc3.example.com&lt;/span&gt;.          IN      A       &lt;span class="highlight"&gt;10.128.20.12&lt;/span&gt;

; 10.128.0.0/16 - A records
&lt;span class="highlight"&gt;host1.nyc3.example.com&lt;/span&gt;.        IN      A      &lt;span class="highlight"&gt;10.128.100.101&lt;/span&gt;
&lt;span class="highlight"&gt;host2.nyc3.example.com&lt;/span&gt;.        IN      A      &lt;span class="highlight"&gt;10.128.200.102&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save and close the &lt;code&gt;db.nyc3.example.com&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Our final example forward zone file looks like the following:&lt;/p&gt;
&lt;div class="code-label " title="/etc/bind/zones/db.nyc3.example.com — updated"&gt;/etc/bind/zones/db.nyc3.example.com — updated&lt;/div&gt;&lt;pre class="code-pre  second-environment"&gt;&lt;code langs=""&gt;$TTL    604800
@       IN      SOA     &lt;span class="highlight"&gt;ns1.nyc3.example.com&lt;/span&gt;. admin.&lt;span class="highlight"&gt;nyc3.example.com&lt;/span&gt;. (
                  &lt;span class="highlight"&gt;3&lt;/span&gt;     ; Serial
             604800     ; Refresh
              86400     ; Retry
            2419200     ; Expire
             604800 )   ; Negative Cache TTL
;
; name servers - NS records
     IN      NS      ns1.&lt;span class="highlight"&gt;nyc3.example.com&lt;/span&gt;.
     IN      NS      ns2.&lt;span class="highlight"&gt;nyc3.example.com&lt;/span&gt;.

; name servers - A records
ns1.&lt;span class="highlight"&gt;nyc3.example.com&lt;/span&gt;.          IN      A       &lt;span class="highlight"&gt;10.128.10.11&lt;/span&gt;
ns2.&lt;span class="highlight"&gt;nyc3.example.com&lt;/span&gt;.          IN      A       &lt;span class="highlight"&gt;10.128.20.12&lt;/span&gt;

; 10.128.0.0/16 - A records
&lt;span class="highlight"&gt;host1.nyc3.example.com&lt;/span&gt;.        IN      A      &lt;span class="highlight"&gt;10.128.100.101&lt;/span&gt;
&lt;span class="highlight"&gt;host2.nyc3.example.com&lt;/span&gt;.        IN      A      &lt;span class="highlight"&gt;10.128.200.102&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now let's move onto the reverse zone file(s).&lt;/p&gt;

&lt;h3 id="creating-the-reverse-zone-file-s"&gt;Creating the Reverse Zone File(s)&lt;/h3&gt;

&lt;p&gt;Reverse zone files are where we define DNS PTR records for reverse DNS lookups.  That is, when the DNS receives a query by IP address, "10.128.100.101" for example, it will look in the reverse zone file(s) to resolve the corresponding FQDN, "host1.nyc3.example.com" in this case.&lt;/p&gt;

&lt;p&gt;On &lt;strong&gt;ns1&lt;/strong&gt;, for each reverse zone specified in the &lt;code&gt;named.conf.local&lt;/code&gt; file, create a reverse zone file.  We will base our reverse zone file(s) on the sample &lt;code&gt;db.127&lt;/code&gt; zone file.  Copy it to the proper location with the following commands (substituting the destination filename so it matches your reverse zone definition):&lt;/p&gt;
&lt;pre class="code-pre custom_prefix second-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="ns1$"&gt;sudo cp /etc/bind/db.127 /etc/bind/zones/db.&lt;span class="highlight"&gt;10.128&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Edit the reverse zone file that corresponds to the reverse zone(s) defined in &lt;code&gt;named.conf.local&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix second-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="ns1$"&gt;sudo nano /etc/bind/zones/db.&lt;span class="highlight"&gt;10.128&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Initially, it will look something like the following:&lt;/p&gt;
&lt;div class="code-label " title="/etc/bind/zones/db.10.128 — original"&gt;/etc/bind/zones/db.10.128 — original&lt;/div&gt;&lt;pre class="code-pre  second-environment"&gt;&lt;code langs=""&gt;$TTL    604800
@       IN      SOA     localhost. root.localhost. (
                              1         ; Serial
                         604800         ; Refresh
                          86400         ; Retry
                        2419200         ; Expire
                         604800 )       ; Negative Cache TTL
;
@       IN      NS      localhost.      ; delete this line
1.0.0   IN      PTR     localhost.      ; delete this line
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the same manner as the forward zone file, you will want to edit the SOA record and increment the &lt;strong&gt;serial&lt;/strong&gt; value.  It should look something like this:&lt;/p&gt;
&lt;div class="code-label " title="/etc/bind/zones/db.10.128 — updated 1 of 3"&gt;/etc/bind/zones/db.10.128 — updated 1 of 3&lt;/div&gt;&lt;pre class="code-pre  second-environment"&gt;&lt;code langs=""&gt;@       IN      SOA     &lt;span class="highlight"&gt;ns1.nyc3.example.com&lt;/span&gt;. &lt;span class="highlight"&gt;admin&lt;/span&gt;.&lt;span class="highlight"&gt;nyc3.example.com&lt;/span&gt;. (
                              &lt;span class="highlight"&gt;3&lt;/span&gt;         ; Serial

                              . . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now delete the two records at the end of the file (after the SOA record).  If you're not sure which lines to delete, they are marked with a "delete this line" comment above.&lt;/p&gt;

&lt;p&gt;At the end of the file, add your name server records with the following lines (replace the names with your own).  Note that the second column specifies that these are "NS" records:&lt;/p&gt;
&lt;div class="code-label " title="/etc/bind/zones/db.10.128 — updated 2 of 3"&gt;/etc/bind/zones/db.10.128 — updated 2 of 3&lt;/div&gt;&lt;pre class="code-pre  second-environment"&gt;&lt;code langs=""&gt;. . .

; name servers - NS records
      IN      NS      ns1.&lt;span class="highlight"&gt;nyc3.example.com&lt;/span&gt;.
      IN      NS      ns2.&lt;span class="highlight"&gt;nyc3.example.com&lt;/span&gt;.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then add &lt;code&gt;PTR&lt;/code&gt; records for all of your servers whose IP addresses are on the subnet of the zone file that you are editing.  In our example, this includes all of our hosts because they are all on the &lt;code&gt;10.128.0.0/16&lt;/code&gt; subnet.  Note that the first column consists of the last two octets of your servers' private IP addresses in &lt;strong&gt;reversed order&lt;/strong&gt;.  Be sure to substitute names and private IP addresses to match your servers:&lt;/p&gt;
&lt;div class="code-label " title="/etc/bind/zones/db.10.128 — updated 3 of 3"&gt;/etc/bind/zones/db.10.128 — updated 3 of 3&lt;/div&gt;&lt;pre class="code-pre  second-environment"&gt;&lt;code langs=""&gt;. . .

; PTR Records
&lt;span class="highlight"&gt;11.10&lt;/span&gt;   IN      PTR     ns1.&lt;span class="highlight"&gt;nyc3.example.com&lt;/span&gt;.    ; 10.128.10.11
&lt;span class="highlight"&gt;12.20&lt;/span&gt;   IN      PTR     ns2.&lt;span class="highlight"&gt;nyc3.example.com&lt;/span&gt;.    ; 10.128.20.12
&lt;span class="highlight"&gt;101.100&lt;/span&gt; IN      PTR     &lt;span class="highlight"&gt;host1.nyc3.example.com&lt;/span&gt;.  ; 10.128.100.101
&lt;span class="highlight"&gt;102.200&lt;/span&gt; IN      PTR     &lt;span class="highlight"&gt;host2.nyc3.example.com&lt;/span&gt;.  ; 10.128.200.102
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save and close the reverse zone file (repeat this section if you need to add more reverse zone files).&lt;/p&gt;

&lt;p&gt;Our final example reverse zone file looks like the following:&lt;/p&gt;
&lt;div class="code-label " title="/etc/bind/zones/db.10.128 — updated"&gt;/etc/bind/zones/db.10.128 — updated&lt;/div&gt;&lt;pre class="code-pre  second-environment"&gt;&lt;code langs=""&gt;$TTL    604800
@       IN      SOA     &lt;span class="highlight"&gt;nyc3.example.com&lt;/span&gt;. admin.nyc3.example.com. (
                              &lt;span class="highlight"&gt;3&lt;/span&gt;         ; Serial
                         604800         ; Refresh
                          86400         ; Retry
                        2419200         ; Expire
                         604800 )       ; Negative Cache TTL
; name servers
      IN      NS      ns1.&lt;span class="highlight"&gt;nyc3.example.com&lt;/span&gt;.
      IN      NS      ns2.&lt;span class="highlight"&gt;nyc3.example.com&lt;/span&gt;.

; PTR Records
&lt;span class="highlight"&gt;11.10&lt;/span&gt;   IN      PTR     ns1.&lt;span class="highlight"&gt;nyc3.example.com&lt;/span&gt;.    ; 10.128.10.11
&lt;span class="highlight"&gt;12.20&lt;/span&gt;   IN      PTR     ns2.&lt;span class="highlight"&gt;nyc3.example.com&lt;/span&gt;.    ; 10.128.20.12
&lt;span class="highlight"&gt;101.100&lt;/span&gt; IN      PTR     &lt;span class="highlight"&gt;host1.nyc3.example.com&lt;/span&gt;.  ; 10.128.100.101
&lt;span class="highlight"&gt;102.200&lt;/span&gt; IN      PTR     &lt;span class="highlight"&gt;host2.nyc3.example.com&lt;/span&gt;.  ; 10.128.200.102
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We're done editing our files, so next we can check our files for errors.&lt;/p&gt;

&lt;h3 id="checking-the-bind-configuration-syntax"&gt;Checking the BIND Configuration Syntax&lt;/h3&gt;

&lt;p&gt;Run the following command to check the syntax of the &lt;code&gt;named.conf*&lt;/code&gt; files:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix second-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="ns1$"&gt;sudo named-checkconf
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If your named configuration files have no syntax errors, you will return to your shell prompt and see no error messages.  If there are problems with your configuration files, review the error message and the "Configure Primary DNS Server" section, then try &lt;code&gt;named-checkconf&lt;/code&gt; again.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;named-checkzone&lt;/code&gt; command can be used to check the correctness of your zone files.  Its first argument specifies a zone name, and the second argument specifies the corresponding zone file, which are both defined in &lt;code&gt;named.conf.local&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For example, to check the "&lt;span class="highlight"&gt;nyc3.example.com&lt;/span&gt;" forward zone configuration, run the following command (change the names to match your forward zone and file):&lt;/p&gt;
&lt;pre class="code-pre command second-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo named-checkzone &lt;span class="highlight"&gt;nyc3.example.com&lt;/span&gt; /etc/bind/zones/db.&lt;span class="highlight"&gt;nyc3.example.com&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And to check the "&lt;span class="highlight"&gt;128.10&lt;/span&gt;.in-addr.arpa" reverse zone configuration, run the following command (change the numbers to match your reverse zone and file):&lt;/p&gt;
&lt;pre class="code-pre command second-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo named-checkzone &lt;span class="highlight"&gt;128.10&lt;/span&gt;.in-addr.arpa /etc/bind/zones/db.&lt;span class="highlight"&gt;10.128&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When all of your configuration and zone files have no errors in them, you should be ready to restart the BIND service.&lt;/p&gt;

&lt;h3 id="restarting-bind"&gt;Restarting BIND&lt;/h3&gt;

&lt;p&gt;Restart BIND:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix second-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="ns1$"&gt;sudo systemctl restart bind9
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you have the UFW firewall configured, open up access to BIND by typing:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix second-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="ns1$"&gt;sudo ufw allow Bind9
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Your primary DNS server is now setup and ready to respond to DNS queries.  Let's move on to creating the secondary DNS server.&lt;/p&gt;

&lt;h2 id="configuring-the-secondary-dns-server"&gt;Configuring the Secondary DNS Server&lt;/h2&gt;

&lt;p&gt;In most environments, it is a good idea to set up a secondary DNS server that will respond to requests if the primary becomes unavailable.  Luckily, the secondary DNS server is much easier to configure.&lt;/p&gt;

&lt;p&gt;On &lt;strong&gt;ns2&lt;/strong&gt;, edit the &lt;code&gt;named.conf.options&lt;/code&gt; file:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix third-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="ns2$"&gt;sudo nano /etc/bind/named.conf.options
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At the top of the file, add the ACL with the private IP addresses of all of your trusted servers:&lt;/p&gt;
&lt;div class="code-label " title="/etc/bind/named.conf.options — updated 1 of 2 (secondary)"&gt;/etc/bind/named.conf.options — updated 1 of 2 (secondary)&lt;/div&gt;&lt;pre class="code-pre  third-environment"&gt;&lt;code langs=""&gt;acl "trusted" {
        &lt;span class="highlight"&gt;10.128.10.11&lt;/span&gt;;   # ns1
        &lt;span class="highlight"&gt;10.128.20.12&lt;/span&gt;;   # ns2 - can be set to localhost
        &lt;span class="highlight"&gt;10.128.100.101&lt;/span&gt;;  # host1
        &lt;span class="highlight"&gt;10.128.200.102&lt;/span&gt;;  # host2
};

options {

        . . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Below the &lt;code&gt;directory&lt;/code&gt; directive, add the following lines:&lt;/p&gt;
&lt;div class="code-label " title="/etc/bind/named.conf.options — updated 2 of 2 (secondary)"&gt;/etc/bind/named.conf.options — updated 2 of 2 (secondary)&lt;/div&gt;&lt;pre class="code-pre  third-environment"&gt;&lt;code langs=""&gt;        recursion yes;
        allow-recursion { trusted; };
        listen-on { &lt;span class="highlight"&gt;10.128.20.12&lt;/span&gt;; };      # ns2 private IP address
        allow-transfer { none; };          # disable zone transfers by default

        forwarders {
                8.8.8.8;
                8.8.4.4;
        };
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save and close the &lt;code&gt;named.conf.options&lt;/code&gt; file.  This file should look exactly like &lt;strong&gt;ns1&lt;/strong&gt;'s &lt;code&gt;named.conf.options&lt;/code&gt; file except it should be configured to listen on &lt;strong&gt;ns2&lt;/strong&gt;'s private IP address.&lt;/p&gt;

&lt;p&gt;Now edit the &lt;code&gt;named.conf.local&lt;/code&gt; file:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix third-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="ns2$"&gt;sudo nano /etc/bind/named.conf.local
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Define slave zones that correspond to the master zones on the primary DNS server.  Note that the type is "slave", the file does not contain a path, and there is a &lt;code&gt;masters&lt;/code&gt; directive which should be set to the primary DNS server's private IP address.  If you defined multiple reverse zones in the primary DNS server, make sure to add them all here:&lt;/p&gt;
&lt;div class="code-label " title="/etc/bind/named.conf.local — updated (secondary)"&gt;/etc/bind/named.conf.local — updated (secondary)&lt;/div&gt;&lt;pre class="code-pre  third-environment"&gt;&lt;code langs=""&gt;zone "&lt;span class="highlight"&gt;nyc3.example.com&lt;/span&gt;" {
    type slave;
    file "db.&lt;span class="highlight"&gt;nyc3.example.com&lt;/span&gt;";
    masters { &lt;span class="highlight"&gt;10.128.10.11&lt;/span&gt;; };  # ns1 private IP
};

zone "&lt;span class="highlight"&gt;128.10&lt;/span&gt;.in-addr.arpa" {
    type slave;
    file "db.&lt;span class="highlight"&gt;10.128&lt;/span&gt;";
    masters { &lt;span class="highlight"&gt;10.128.10.11&lt;/span&gt;; };  # ns1 private IP
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now save and close the &lt;code&gt;named.conf.local&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Run the following command to check the validity of your configuration files:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix third-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="ns2$"&gt;sudo named-checkconf
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once that checks out, restart BIND:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix third-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="ns2$"&gt;sudo systemctl restart bind9
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Allow DNS connections to the server by altering the UFW firewall rules:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix third-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="ns2$"&gt;sudo ufw allow Bind9
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now you have primary and secondary DNS servers for private network name and IP address resolution.  Now you must configure your client servers to use your private DNS servers.&lt;/p&gt;

&lt;h2 id="configuring-dns-clients"&gt;Configuring DNS Clients&lt;/h2&gt;

&lt;p&gt;Before all of your servers in the "trusted" ACL can query your DNS servers, you must configure each of them to use &lt;strong&gt;ns1&lt;/strong&gt; and &lt;strong&gt;ns2&lt;/strong&gt; as name servers.  This process varies depending on OS, but for most Linux distributions it involves adding your name servers to the &lt;code&gt;/etc/resolv.conf&lt;/code&gt; file.&lt;/p&gt;

&lt;h3 id="ubuntu-18-04-clients"&gt;Ubuntu 18.04 Clients&lt;/h3&gt;

&lt;p&gt;On Ubuntu 18.04, networking is configured with Netplan, an abstraction that allows you to write standardized network configuration and apply it to incompatible backend networking software.  To configure DNS, we need to write a Netplan configuration file.&lt;/p&gt;

&lt;p&gt;First, find the device associated with your private network by querying the private subnet with the &lt;code&gt;ip address&lt;/code&gt; command:&lt;/p&gt;
&lt;pre class="code-pre command fourth-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;ip address show to &lt;span class="highlight"&gt;10.128.0.0/16&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre  fourth-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;3: &lt;span class="highlight"&gt;eth1&lt;/span&gt;: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1500 qdisc fq_codel state UP group default qlen 1000
    inet 10.128.100.101/16 brd 10.128.255.255 scope global eth1
           valid_lft forever preferred_lft forever
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, the private interface is &lt;code&gt;eth1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Next, create a new file in &lt;code&gt;/etc/netplan&lt;/code&gt; called &lt;code&gt;00-private-nameservers.yaml&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command fourth-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/netplan/00-private-nameservers.yaml
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Inside, paste the following contents.  You will need to modify the interface of the private network, the addresses of your &lt;strong&gt;ns1&lt;/strong&gt; and &lt;strong&gt;ns2&lt;/strong&gt; DNS servers, and the DNS zone:&lt;/p&gt;

&lt;p&gt;&lt;span class='note'&gt;&lt;strong&gt;Note:&lt;/strong&gt; Netplan uses the &lt;a href="http://yaml.org/"&gt;YAML data serialization format&lt;/a&gt; for its configuration files.  Because YAML uses indentation and whitespace to define its data structure, make sure that your definition uses consistent indentation to avoid errors.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;
&lt;div class="code-label " title="/etc/netplan 00-private-nameservers.yaml"&gt;/etc/netplan 00-private-nameservers.yaml&lt;/div&gt;&lt;pre class="code-pre  fourth-environment"&gt;&lt;code langs=""&gt;network:
    version: 2
    ethernets:
        &lt;span class="highlight"&gt;eth1&lt;/span&gt;:                                 # Private network interface
            nameservers:
                addresses:
                - &lt;span class="highlight"&gt;10.128.10.11&lt;/span&gt;                # Private IP for ns1
                - &lt;span class="highlight"&gt;10.132.20.12&lt;/span&gt;                # Private IP for ns2
                search: [ &lt;span class="highlight"&gt;nyc3.example.com&lt;/span&gt; ]  # DNS zone

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save and close the file when you are finished.&lt;/p&gt;

&lt;p&gt;Next, tell Netplan to attempt to use the new configuration file by using &lt;code&gt;netplan try&lt;/code&gt;.  If there are problems that cause a loss of networking, Netplan will automatically roll back the changes after a timeout:&lt;/p&gt;
&lt;pre class="code-pre command fourth-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo netplan try
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre  fourth-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Warning: Stopping systemd-networkd.service, but it can still be activated by:
  systemd-networkd.socket
Do you want to keep these settings?


Press ENTER before the timeout to accept the new configuration


Changes will revert in 120 seconds
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If the countdown is updating correctly at the bottom, the new configuration is at least functional enough to not break your SSH connection.  Press &lt;strong&gt;ENTER&lt;/strong&gt; to accept the new configuration.&lt;/p&gt;

&lt;p&gt;Now, check that the system's DNS resolver to determine if your DNS configuration has been applied:&lt;/p&gt;
&lt;pre class="code-pre command fourth-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemd-resolve --status
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Scroll down until you see the section for your private network interface.  You should see the private IP addresses for your DNS servers listed first, followed by some fallback values.  Your domain should should be in the "DNS Domain":&lt;/p&gt;
&lt;pre class="code-pre  fourth-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;. . .
Link 3 (eth1)
      Current Scopes: DNS
       LLMNR setting: yes
MulticastDNS setting: no
      DNSSEC setting: no
    DNSSEC supported: no
         DNS Servers: &lt;span class="highlight"&gt;10.128.10.11&lt;/span&gt;
                      &lt;span class="highlight"&gt;10.128.20.12&lt;/span&gt;
                      67.207.67.2
                      67.207.67.3
          DNS Domain: &lt;span class="highlight"&gt;nyc3.example.com&lt;/span&gt;
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Your client should now be configured to use your internal DNS servers.&lt;/p&gt;

&lt;h3 id="ubuntu-16-04-and-debian-clients"&gt;Ubuntu 16.04 and Debian Clients&lt;/h3&gt;

&lt;p&gt;On Ubuntu 16.04 and Debian Linux servers, you can edit the &lt;code&gt;/etc/network/interfaces&lt;/code&gt; file:&lt;/p&gt;
&lt;pre class="code-pre command fourth-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/network/interfaces
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Inside, find the &lt;code&gt;dns-nameservers&lt;/code&gt; line.  If it is attached to the &lt;code&gt;lo&lt;/code&gt; interface, move it to your networking interface (&lt;code&gt;eth0&lt;/code&gt; or &lt;code&gt;eth1&lt;/code&gt; for example).  Next, prepend your own name servers in front of the list that is currently there.  Below that line, add a &lt;code&gt;dns-search&lt;/code&gt; option pointed to the base domain of your infrastructure.  In our case, this would be "nyc3.example.com":&lt;/p&gt;
&lt;div class="code-label " title="/etc/network/interfaces"&gt;/etc/network/interfaces&lt;/div&gt;&lt;pre class="code-pre  fourth-environment"&gt;&lt;code langs=""&gt;    . . .

    dns-nameservers &lt;span class="highlight"&gt;10.128.10.11&lt;/span&gt; &lt;span class="highlight"&gt;10.128.20.12&lt;/span&gt; 8.8.8.8
    &lt;span class="highlight"&gt;dns-search nyc3.example.com&lt;/span&gt;

    . . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save and close the file when you are finished.&lt;/p&gt;

&lt;p&gt;Make sure that the &lt;code&gt;resolvconf&lt;/code&gt; package is installed on your system:&lt;/p&gt;
&lt;pre class="code-pre command fourth-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt update
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo apt install resolvconf
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, restart your networking services, applying the new changes with the following commands.  Make sure you replace &lt;code&gt;eth0&lt;/code&gt; with the name of your networking interface:&lt;/p&gt;
&lt;pre class="code-pre command fourth-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ifdown --force &lt;span class="highlight"&gt;eth0&lt;/span&gt; &amp;amp;&amp;amp; sudo ip addr flush dev &lt;span class="highlight"&gt;eth0&lt;/span&gt; &amp;amp;&amp;amp; sudo ifup --force &lt;span class="highlight"&gt;eth0&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This should restart your network without dropping your current connection.  If it worked correctly, you should see something like this:&lt;/p&gt;
&lt;pre class="code-pre  fourth-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;RTNETLINK answers: No such process
Waiting for DAD... Done
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Double check that your settings were applied by typing:&lt;/p&gt;
&lt;pre class="code-pre command fourth-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cat /etc/resolv.conf
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see your name servers in the &lt;code&gt;/etc/resolv.conf&lt;/code&gt; file, as well as your search domain:&lt;/p&gt;
&lt;pre class="code-pre  fourth-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;# Dynamic resolv.conf(5) file for glibc resolver(3) generated by resolvconf(8)
#     DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN
nameserver 10.128.10.11
nameserver 10.128.20.12
nameserver 8.8.8.8
search nyc3.example.com
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Your client is now configured to use your DNS servers.&lt;/p&gt;

&lt;h3 id="centos-clients"&gt;CentOS Clients&lt;/h3&gt;

&lt;p&gt;On CentOS, RedHat, and Fedora Linux, edit the &lt;code&gt;/etc/sysconfig/network-scripts/ifcfg-&lt;span class="highlight"&gt;eth0&lt;/span&gt;&lt;/code&gt; file.  You may have to substitute &lt;code&gt;eth0&lt;/code&gt; with the name of your primary network interface:&lt;/p&gt;
&lt;pre class="code-pre command fourth-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/sysconfig/network-scripts/ifcfg-&lt;span class="highlight"&gt;eth0&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Search for the &lt;code&gt;DNS1&lt;/code&gt; and &lt;code&gt;DNS2&lt;/code&gt; options and set them to the private IP addresses of your primary and secondary name servers.  Add a &lt;code&gt;DOMAIN&lt;/code&gt; parameter that with your infrastructure's base domain.  In this guide, that would be "nyc3.example.com":&lt;/p&gt;
&lt;div class="code-label " title="/etc/sysconfig/network-scripts/ifcfg-eth0"&gt;/etc/sysconfig/network-scripts/ifcfg-eth0&lt;/div&gt;&lt;pre class="code-pre  fourth-environment"&gt;&lt;code langs=""&gt;. . .
DNS1=&lt;span class="highlight"&gt;10.128.10.11&lt;/span&gt;
DNS2=&lt;span class="highlight"&gt;10.128.20.12&lt;/span&gt;
&lt;span class="highlight"&gt;DOMAIN='nyc3.example.com'&lt;/span&gt;
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save and close the file when you are finished.&lt;/p&gt;

&lt;p&gt;Now, restart the networking service by typing:&lt;/p&gt;
&lt;pre class="code-pre command fourth-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl restart network
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The command may hang for a few seconds, but should return you to the prompt shortly.&lt;/p&gt;

&lt;p&gt;Check that your changes were applied by typing:&lt;/p&gt;
&lt;pre class="code-pre command fourth-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cat /etc/resolv.conf
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see your name servers and search domain in the list:&lt;/p&gt;
&lt;div class="code-label " title="/etc/resolv.conf"&gt;/etc/resolv.conf&lt;/div&gt;&lt;pre class="code-pre  fourth-environment"&gt;&lt;code langs=""&gt;nameserver 10.128.10.11
nameserver 10.128.20.12
search nyc3.example.com
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Your client should now be able to connect to and use your DNS servers.&lt;/p&gt;

&lt;h2 id="testing-clients"&gt;Testing Clients&lt;/h2&gt;

&lt;p&gt;Use &lt;code&gt;nslookup&lt;/code&gt; to test if your clients can query your name servers. You should be able to do this on all of the clients that you have configured and are in the "trusted" ACL.&lt;/p&gt;

&lt;p&gt;For CentOS clients, you may need to install the utility with:&lt;/p&gt;
&lt;pre class="code-pre command fourth-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo yum install bind-utils
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For Debian clients, you can install with:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install dnsutils
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can start by performing a forward lookup.&lt;/p&gt;

&lt;h3 id="forward-lookup"&gt;Forward Lookup&lt;/h3&gt;

&lt;p&gt;For example, we can perform a forward lookup to retrieve the IP address of &lt;strong&gt;host1.nyc3.example.com&lt;/strong&gt; by running the following command:&lt;/p&gt;
&lt;pre class="code-pre command fourth-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nslookup host1
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Querying "host1" expands to "host1.nyc3.example.com because of the &lt;code&gt;search&lt;/code&gt; option is set to your private subdomain, and DNS queries will attempt to look on that subdomain before looking for the host elsewhere. The output of the command above would look like the following:&lt;/p&gt;
&lt;pre class="code-pre  fourth-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Server:     127.0.0.53
Address:    127.0.0.53#53

Non-authoritative answer:
Name:   host1.nyc3.example.com
Address: 10.128.100.101
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, we can check reverse lookups.&lt;/p&gt;

&lt;h3 id="reverse-lookup"&gt;Reverse Lookup&lt;/h3&gt;

&lt;p&gt;To test the reverse lookup, query the DNS server with &lt;strong&gt;host1&lt;/strong&gt;'s private IP address:&lt;/p&gt;
&lt;pre class="code-pre command fourth-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nslookup 10.128.100.101
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see output that looks like the following:&lt;/p&gt;
&lt;pre class="code-pre  fourth-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;11.10.128.10.in-addr.arpa   name = host1.nyc3.example.com.

Authoritative answers can be found from:
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If all of the names and IP addresses resolve to the correct values, that means that your zone files are configured properly.  If you receive unexpected values, be sure to review the zone files on your primary DNS server (e.g. &lt;code&gt;db.nyc3.example.com&lt;/code&gt; and &lt;code&gt;db.10.128&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Congratulations!  Your internal DNS servers are now set up properly!  Now we will cover maintaining your zone records.&lt;/p&gt;

&lt;h2 id="maintaining-dns-records"&gt;Maintaining DNS Records&lt;/h2&gt;

&lt;p&gt;Now that you have a working internal DNS, you need to maintain your DNS records so they accurately reflect your server environment.&lt;/p&gt;

&lt;h3 id="adding-a-host-to-dns"&gt;Adding a Host to DNS&lt;/h3&gt;

&lt;p&gt;Whenever you add a host to your environment (in the same datacenter), you will want to add it to DNS.  Here is a list of steps that you need to take:&lt;/p&gt;

&lt;h4 id="primary-name-server"&gt;Primary Name Server&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Forward zone file: Add an "A" record for the new host, increment the value of "Serial"&lt;/li&gt;
&lt;li&gt;Reverse zone file: Add a "PTR" record for the new host, increment the value of "Serial"&lt;/li&gt;
&lt;li&gt;Add your new host's private IP address to the "trusted" ACL (&lt;code&gt;named.conf.options&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Test your configuration files:&lt;/p&gt;
&lt;pre class="code-pre command second-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo named-checkconf
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo named-checkzone &lt;span class="highlight"&gt;nyc3.example.com&lt;/span&gt; db.&lt;span class="highlight"&gt;nyc3.example.com&lt;/span&gt;
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo named-checkzone &lt;span class="highlight"&gt;128.10&lt;/span&gt;.in-addr.arpa /etc/bind/zones/db.&lt;span class="highlight"&gt;10.128&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then reload BIND:&lt;/p&gt;
&lt;pre class="code-pre command second-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl reload bind9
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Your primary server should be configured for the new host now.&lt;/p&gt;

&lt;h4 id="secondary-name-server"&gt;Secondary Name Server&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Add your new host's private IP address to the "trusted" ACL (&lt;code&gt;named.conf.options&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Check the configuration syntax:&lt;/p&gt;
&lt;pre class="code-pre command third-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo named-checkconf
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then reload BIND:&lt;/p&gt;
&lt;pre class="code-pre command third-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl reload bind9
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Your secondary server will now accept connections from the new host.&lt;/p&gt;

&lt;h4 id="configure-new-host-to-use-your-dns"&gt;Configure New Host to Use Your DNS&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Configure &lt;code&gt;/etc/resolv.conf&lt;/code&gt; to use your DNS servers&lt;/li&gt;
&lt;li&gt;Test using &lt;code&gt;nslookup&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id="removing-host-from-dns"&gt;Removing Host from DNS&lt;/h3&gt;

&lt;p&gt;If you remove a host from your environment or want to just take it out of DNS, just remove all the things that were added when you added the server to DNS (i.e. the reverse of the steps above).&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Now you may refer to your servers' private network interfaces by name, rather than by IP address.  This makes configuration of services and applications easier because you no longer have to remember the private IP addresses, and the files will be easier to read and understand.  Also, now you can change your configurations to point to a new servers in a single place, your primary DNS server, instead of having to edit a variety of distributed configuration files, which eases maintenance.&lt;/p&gt;

&lt;p&gt;Once you have your internal DNS set up, and your configuration files are using private FQDNs to specify network connections, it is &lt;strong&gt;critical&lt;/strong&gt; that your DNS servers are properly maintained.  If they both become unavailable, your services and applications that rely on them will cease to function properly.  This is why it is recommended to set up your DNS with at least one secondary server, and to maintain working backups of all of them.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-install-the-anaconda-python-distribution-on-debian-9</id>
    <published>2018-09-06T18:37:52Z</published>
    <updated>2018-09-06T18:38:25Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-install-the-anaconda-python-distribution-on-debian-9"/>
    <title>How To Install the Anaconda Python Distribution on Debian 9</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;Anaconda is an open-source package manager, environment manager, and distribution of the Python and R programming languages. Designed for data science and machine learning workflows, it is commonly used for large-scale data processing, scientific computing, and predictive analytics.&lt;/p&gt;

&lt;p&gt;Available in both free and paid enterprise versions, Anaconda offers a collection of over 1,000 data science packages. The Anaconda distribution ships with the &lt;code&gt;conda&lt;/code&gt; command-line utility. You can learn more about Anaconda and &lt;code&gt;conda&lt;/code&gt; by reading the official &lt;a href="https://docs.anaconda.com/"&gt;Anaconda Documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This tutorial will guide you through installing the Python 3 version of Anaconda on a Debian 9 server. &lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;Before you begin with this guide, you should have a non-root user with sudo privileges set up on your server. &lt;/p&gt;

&lt;p&gt;You can achieve this prerequisite by completing our &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-debian-9"&gt;Debian 9 initial server setup guide&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id="installing-anaconda"&gt;Installing Anaconda&lt;/h2&gt;

&lt;p&gt;The best way to install Anaconda is to download the latest Anaconda installer bash script, verify it, and then run it.&lt;/p&gt;

&lt;p&gt;Find the latest version of Anaconda for Python 3 at the &lt;strong&gt;Downloads&lt;/strong&gt; page accessible via the &lt;a href="https://www.anaconda.com"&gt;Anaconda home page&lt;/a&gt;. At the time of writing, the latest version is 5.2, but you should use a later stable version if it is available. &lt;/p&gt;

&lt;p&gt;Next, change to the &lt;code&gt;/tmp&lt;/code&gt; directory on your server. This is a good directory to download ephemeral items, like the Anaconda bash script, which we won't need after running it.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cd /tmp
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We’ll use the &lt;code&gt;curl&lt;/code&gt; command-line tool to download the script. Install &lt;code&gt;curl&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install curl
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, use &lt;code&gt;curl&lt;/code&gt; to download the link that you copied from the Anaconda website:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;curl -O https://repo.anaconda.com/archive/Anaconda3-&lt;span class="highlight"&gt;5.2.0&lt;/span&gt;-Linux-x86_64.sh
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can now verify the data integrity of the installer with cryptographic hash verification through the SHA-256 checksum. We’ll use the &lt;code&gt;sha256sum&lt;/code&gt; command along with the filename of the script:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sha256sum Anaconda3-&lt;span class="highlight"&gt;5.2.0&lt;/span&gt;-Linux-x86_64.sh
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You’ll receive output that looks similar to this:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;&lt;span class="highlight"&gt;09f53738b0cd3bb96f5b1bac488e5528df9906be2480fe61df40e0e0d19e3d48  Anaconda3-5.2.0-Linux-x86_64.sh&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should check the output against the hashes available at the &lt;a href="https://docs.anaconda.com/anaconda/install/hashes/lin-3-64/"&gt;Anaconda with Python 3 on 64-bit Linux page&lt;/a&gt; for your appropriate Anaconda version. As long as your output matches the hash displayed in the &lt;code&gt;sha2561&lt;/code&gt; row, you’re good to go.&lt;/p&gt;

&lt;p&gt;Now we can run the script:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;bash Anaconda3-&lt;span class="highlight"&gt;5.2.0&lt;/span&gt;-Linux-x86_64.sh
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You’ll receive the following output:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;
Welcome to Anaconda3 5.2.0

In order to continue the installation process, please review the license
agreement.
Please, press ENTER to continue
&amp;gt;&amp;gt;&amp;gt; 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Press &lt;code&gt;ENTER&lt;/code&gt; to continue and then press &lt;code&gt;ENTER&lt;/code&gt; to read through the license. Once you’re done reading the license, you’ll be prompted to approve the license terms:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Do you approve the license terms? [yes|no]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As long as you agree, type &lt;code&gt;yes&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;At this point, you’ll be prompted to choose the location of the installation. You can press &lt;code&gt;ENTER&lt;/code&gt; to accept the default location, or specify a different location to modify it.&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Anaconda3 will now be installed into this location:
/home/&lt;span class="highlight"&gt;sammy&lt;/span&gt;/anaconda3

  - Press ENTER to confirm the location
  - Press CTRL-C to abort the installation
  - Or specify a different location below

[/home/&lt;span class="highlight"&gt;sammy&lt;/span&gt;/anaconda3] &amp;gt;&amp;gt;&amp;gt; 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The installation process will continue. Note that it may take some time. &lt;/p&gt;

&lt;p&gt;Once installation is complete, you’ll receive the following output:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;...
installation finished.
Do you wish the installer to prepend the Anaconda3 install location
to PATH in your /home/sammy/.bashrc ? [yes|no]
[no] &amp;gt;&amp;gt;&amp;gt; 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Type &lt;code&gt;yes&lt;/code&gt; so that you can use the &lt;code&gt;conda&lt;/code&gt; command. You’ll receive the following output next:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Appending source /home/sammy/anaconda3/bin/activate to /home/sammy/.bashrc
A backup will be made to: /home/sammy/.bashrc-anaconda3.bak
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, you’ll receive the following prompt regarding whether or not you would like to download Visual Studio Code (or VSCode), a free and open-source editor for code developed by Microsoft that can run on Linux. You can learn more about the editor on the &lt;a href="https://code.visualstudio.com/"&gt;official Visual Studio Code&lt;/a&gt; website. &lt;/p&gt;

&lt;p&gt;At this point, you can decide whether or not to download the editor now by typing &lt;code&gt;yes&lt;/code&gt; or &lt;code&gt;no&lt;/code&gt;.&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;Anaconda is partnered with Microsoft! Microsoft VSCode is a streamlined
code editor with support for development operations like debugging, task
running and version control.

To install Visual Studio Code, you will need:
  - Administrator Privileges
  - Internet connectivity

Visual Studio Code License: https://code.visualstudio.com/license

Do you wish to proceed with the installation of Microsoft VSCode? [yes|no]
&amp;gt;&amp;gt;&amp;gt; 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In order to activate the installation, you should source the &lt;code&gt;~/.bashrc&lt;/code&gt; file:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;source ~/.bashrc
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once you have done that, you can verify your install by making use of the &lt;code&gt;conda&lt;/code&gt; command, for example with &lt;code&gt;list&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;conda list
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You’ll receive output of all the packages you have available through the Anaconda installation:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;# packages in environment at /home/sammy/anaconda3:
#
# Name                    Version                   Build  Channel
_ipyw_jlab_nb_ext_conf    0.1.0            py36he11e457_0  
alabaster                 0.7.10           py36h306e16b_0  
anaconda                  5.2.0                    py36_3  
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that Anaconda is installed, we can go on to setting up Anaconda environments.&lt;/p&gt;

&lt;h2 id="setting-up-anaconda-environments"&gt;Setting Up Anaconda Environments&lt;/h2&gt;

&lt;p&gt;Anaconda virtual environments allow you to keep projects organized by Python versions and packages needed. For each Anaconda environment you set up, you can specify which version of Python to use and can keep all of your related programming files together within that directory.&lt;/p&gt;

&lt;p&gt;First, we can check to see which versions of Python are available for us to use:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;conda search "^python$"
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You’ll receive output with the different versions of Python that you can target, including both Python 3 and Python 2 versions. Since we are using the Anaconda with Python 3 in this tutorial, you will have access only to the Python 3 versions of packages.&lt;/p&gt;

&lt;p&gt;Let’s create an environment using the most recent version of Python 3. We can achieve this by assigning version 3 to the &lt;code&gt;python&lt;/code&gt; argument. We’ll call the environment &lt;span class="highlight"&gt;my_env&lt;/span&gt;, but you’ll likely want to use a more descriptive name for your environment especially if you are using environments to access more than one version of Python.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;conda create --name &lt;span class="highlight"&gt;my_env&lt;/span&gt; python=3
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We’ll receive output with information about what is downloaded and which packages will be installed, and then be prompted to proceed with &lt;code&gt;y&lt;/code&gt; or &lt;code&gt;n&lt;/code&gt;. As long as you agree, type &lt;code&gt;y&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;conda&lt;/code&gt; utility will now fetch the packages for the environment and let you know when it’s complete. &lt;/p&gt;

&lt;p&gt;You can activate your new environment by typing the following:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;source activate &lt;span class="highlight"&gt;my_env&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With your environment activated, your command prompt prefix will change:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="(my_env) sammy@ubuntu:~$"&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Within the environment, you can verify that you’re using the version of Python that you had intended to use:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="(my_env) sammy@ubuntu:~$"&gt; python --version
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Python 3.7.0 :: Anaconda, Inc.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When you’re ready to deactivate your Anaconda environment, you can do so by typing:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="(my_env) sammy@ubuntu:~$"&gt;source deactivate
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that you can replace the word &lt;code&gt;source&lt;/code&gt; with &lt;code&gt;.&lt;/code&gt; to achieve the same results.&lt;/p&gt;

&lt;p&gt;To target a more specific version of Python, you can pass a specific version to the &lt;code&gt;python&lt;/code&gt; argument, like &lt;code&gt;3.5&lt;/code&gt;, for example:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;conda create -n my_env35 python=3.5
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can update your version of Python along the same branch (as in updating Python 3.5.1 to Python 3.5.2) within a respective environment with the following command:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="(my_env35) sammy@ubuntu:~$"&gt;conda update python
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you would like to target a more specific version of Python, you can pass that to the &lt;code&gt;python&lt;/code&gt; argument, as in &lt;code&gt;python=3.3.2&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can inspect all of the environments you have set up with this command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;conda info --envs
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;# conda environments:
#
base                  *  /home/sammy/anaconda3
my_env                   /home/sammy/anaconda3/envs/my_env
my_env35                 /home/sammy/anaconda3/envs/my_env35

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The asterisk indicates the current active environment.&lt;/p&gt;

&lt;p&gt;Each environment you create with &lt;code&gt;conda create&lt;/code&gt; will come with several default packages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;openssl&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pip&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;python&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;readline&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;setuptools&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sqlite&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tk&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;wheel&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;xz&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;zlib&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can add additional packages, such as &lt;code&gt;numpy&lt;/code&gt; for example, with the following command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;conda install --name my_env35 &lt;span class="highlight"&gt;numpy&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you know you would like a &lt;code&gt;numpy&lt;/code&gt; environment upon creation, you can target it in your &lt;code&gt;conda create&lt;/code&gt; command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;conda create --name my_env python=3 numpy
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you are no longer working on a specific project and have no further need for the associated environment, you can remove it. To do so, type the following:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;conda remove --name &lt;span class="highlight"&gt;my_env35&lt;/span&gt; --all
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, when you type the &lt;code&gt;conda info --envs&lt;/code&gt; command, the environment that you removed will no longer be listed. &lt;/p&gt;

&lt;h2 id="updating-anaconda"&gt;Updating Anaconda&lt;/h2&gt;

&lt;p&gt;You should regularly ensure that Anaconda is up-to-date so that you are working with all the latest package releases. &lt;/p&gt;

&lt;p&gt;To do this, you should first update the &lt;code&gt;conda&lt;/code&gt; utility:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;conda update conda
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When prompted to do so, type &lt;code&gt;y&lt;/code&gt; to proceed with the update.&lt;/p&gt;

&lt;p&gt;Once the update of &lt;code&gt;conda&lt;/code&gt; is complete, you can update the Anaconda distribution:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;conda update anaconda
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Again when prompted to do so, type &lt;code&gt;y&lt;/code&gt; to proceed.&lt;/p&gt;

&lt;p&gt;This will ensure that you are using the latest releases of &lt;code&gt;conda&lt;/code&gt; and Anaconda.&lt;/p&gt;

&lt;h2 id="uninstalling-anaconda"&gt;Uninstalling Anaconda&lt;/h2&gt;

&lt;p&gt;If you are no longer using Anaconda and find that you need to uninstall it, you should start with the &lt;code&gt;anaconda-clean&lt;/code&gt; module, which will remove configuration files for when you uninstall Anaconda. &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;conda install anaconda-clean
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Type &lt;code&gt;y&lt;/code&gt; when prompted to do so.&lt;/p&gt;

&lt;p&gt;Once it is installed, you can run the following command. You will be prompted to answer &lt;code&gt;y&lt;/code&gt; before deleting each one. If you would prefer not to be prompted, add &lt;code&gt;--yes&lt;/code&gt; to the end of your command:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;anaconda-clean
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will also create a backup folder called &lt;code&gt;.anaconda_backup&lt;/code&gt; in your home directory:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Backup directory: /home/sammy/.anaconda_backup/&lt;span class="highlight"&gt;2018-09-06T183049&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can now remove your entire Anaconda directory by entering the following command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;rm -rf ~/anaconda3
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, you can remove the PATH line from your &lt;code&gt;.bashrc&lt;/code&gt; file that Anaconda added. To do so, first open a text editor such as nano:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nano ~/.bashrc
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then scroll down to the end of the file (if this is a recent install) or type &lt;code&gt;CTRL + W&lt;/code&gt; to search for Anaconda. Delete or comment out the &lt;code&gt;export PATH&lt;/code&gt; line:&lt;/p&gt;
&lt;div class="code-label " title="/home/sammy/.bashrc"&gt;/home/sammy/.bashrc&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;...
# added by Anaconda3 installer
export PATH="/home/sammy/anaconda3/bin:$PATH"
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When you’re done editing the file, type &lt;code&gt;CTRL + X&lt;/code&gt; to exit and &lt;code&gt;y&lt;/code&gt; to save changes. &lt;/p&gt;

&lt;p&gt;Anaconda is now removed from your server.&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;This tutorial walked you through the installation of Anaconda, working with the &lt;code&gt;conda&lt;/code&gt; command-line utility, setting up environments, updating Anaconda, and deleting Anaconda if you no longer need it.&lt;/p&gt;

&lt;p&gt;You can use Anaconda to help you manage workloads for data science, scientific computing, analytics, and large-scale data processing. From here, you can check out our tutorials on &lt;a href="https://www.digitalocean.com/community/tags/data-analysis/tutorials"&gt;data analysis&lt;/a&gt; and &lt;a href="https://www.digitalocean.com/community/tags/machine-learning/tutorials"&gt;machine learning&lt;/a&gt; to learn more about various tools available to use and projects that you can do.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-set-up-a-node-js-application-for-production-on-debian-9</id>
    <published>2018-09-06T17:39:29Z</published>
    <updated>2018-09-26T21:34:06Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-set-up-a-node-js-application-for-production-on-debian-9"/>
    <title>How To Set Up a Node.js Application for Production on Debian 9</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://nodejs.org/en/"&gt;Node.js&lt;/a&gt; is an open-source JavaScript runtime environment for building server-side and networking applications. The platform runs on Linux, macOS, FreeBSD, and Windows. Though you can run Node.js applications at the command line, this tutorial will focus on running them as a service. This means that the applications will restart on reboot or failure and are safe for use in a production environment.&lt;/p&gt;

&lt;p&gt;In this tutorial, you will set up a production-ready Node.js environment on a single Debian 9 server.  This server will run a Node.js application managed by &lt;a href="http://pm2.keymetrics.io/"&gt;PM2&lt;/a&gt;, and provide users with secure access to the application through an &lt;a href="https://nginx.org/"&gt;Nginx&lt;/a&gt; reverse proxy. The Nginx server will offer HTTPS, using a free certificate provided by &lt;a href="https://letsencrypt.org/"&gt;Let's Encrypt&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;This guide assumes that you have the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Debian 9 server setup, as described in the &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-debian-9"&gt;initial server setup guide for Debian 9&lt;/a&gt;. You should have a non-root user with sudo privileges and an active firewall.&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://www.digitalocean.com/docs/networking/dns/quickstart/"&gt;domain name pointed at your server's public IP&lt;/a&gt;. This tutorial will use the domain name &lt;strong&gt;example.com&lt;/strong&gt; throughout.&lt;/li&gt;
&lt;li&gt;Nginx installed, as covered in &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-nginx-on-debian-9"&gt;How To Install Nginx on Debian 9&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Nginx configured with SSL using Let's Encrypt certificates. &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-secure-nginx-with-let-s-encrypt-on-debian-9"&gt;How To Secure Nginx with Let's Encrypt on Debian 9&lt;/a&gt; will walk you through the process.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When you've completed the prerequisites, you will have a server serving your domain's default placeholder page at &lt;code&gt;https://&lt;span class="highlight"&gt;example.com&lt;/span&gt;/&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id="step-1-—-installing-node-js"&gt;Step 1 — Installing Node.js&lt;/h2&gt;

&lt;p&gt;Let's begin by installing the latest LTS release of Node.js, using the &lt;a href="https://github.com/nodesource/distributions"&gt;NodeSource&lt;/a&gt; package archives.&lt;/p&gt;

&lt;p&gt;To install the NodeSource PPA and access its contents, you will first need to update your package index and install &lt;code&gt;curl&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt update
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo apt install curl
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Make sure you're in your home directory, and then use &lt;code&gt;curl&lt;/code&gt; to retrieve the installation script for the Node.js &lt;span class="highlight"&gt;8&lt;/span&gt;.x archives:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cd ~
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;curl -sL https://deb.nodesource.com/setup_&lt;span class="highlight"&gt;8&lt;/span&gt;.x -o nodesource_setup.sh
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can inspect the contents of this script with &lt;code&gt;nano&lt;/code&gt; or your &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-debian-9#step-six-%E2%80%94-completing-optional-configuration"&gt;preferred text editor&lt;/a&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nano nodesource_setup.sh
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When you're done inspecting the script, run it under &lt;code&gt;sudo&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo bash nodesource_setup.sh
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The PPA will be added to your configuration and your local package cache will be updated automatically. After running the setup script from Nodesource, you can install the Node.js package:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install nodejs
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To check which version of Node.js you have installed after these initial steps, type:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nodejs -v
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;v&lt;span class="highlight"&gt;8.11.4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span class='note'&gt;&lt;strong&gt;Note:&lt;/strong&gt; When installing from the NodeSource PPA, the Node.js executable is called &lt;code&gt;nodejs&lt;/code&gt;, rather than &lt;code&gt;node&lt;/code&gt;.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;nodejs&lt;/code&gt; package contains the &lt;code&gt;nodejs&lt;/code&gt; binary as well as &lt;a href="https://www.npmjs.com/"&gt;&lt;code&gt;npm&lt;/code&gt;&lt;/a&gt;, a package manager for Node modules, so you don't need to install &lt;code&gt;npm&lt;/code&gt; separately. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm&lt;/code&gt; uses a configuration file in your home directory to keep track of updates. It will be created the first time you run &lt;code&gt;npm&lt;/code&gt;. Execute this command to verify that &lt;code&gt;npm&lt;/code&gt; is installed and to create the configuration file:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;npm -v
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;&lt;span class="highlight"&gt;5.6.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In order for some &lt;code&gt;npm&lt;/code&gt; packages to work (those that require compiling code from source, for example), you will need to install the &lt;code&gt;build-essential&lt;/code&gt; package:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install build-essential
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You now have the necessary tools to work with &lt;code&gt;npm&lt;/code&gt; packages that require compiling code from source.&lt;/p&gt;

&lt;p&gt;With the Node.js runtime installed, let's move on to writing a Node.js application.&lt;/p&gt;

&lt;h2 id="step-2-—-creating-a-node-js-application"&gt;Step 2 — Creating a Node.js Application&lt;/h2&gt;

&lt;p&gt;Let's write a &lt;em&gt;Hello World&lt;/em&gt; application that returns "Hello World" to any HTTP requests. This sample application will help you get Node.js set up. You can replace it with your own application — just make sure that you modify your application to listen on the appropriate IP addresses and ports.&lt;/p&gt;

&lt;p&gt;First, let's create a sample application called &lt;code&gt;hello.js&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cd ~
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;nano hello.js
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Insert the following code into the file:&lt;/p&gt;
&lt;div class="code-label " title="~/hello.js"&gt;~/hello.js&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-js"&gt;const http = require('http');

const hostname = 'localhost';
const port = 3000;

const server = http.createServer((req, res) =&amp;gt; {
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  res.end('Hello World!\n');
});

server.listen(port, hostname, () =&amp;gt; {
  console.log(`Server running at http://${hostname}:${port}/`);
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save the file and exit the editor.&lt;/p&gt;

&lt;p&gt;This Node.js application listens on the specified address (&lt;code&gt;localhost&lt;/code&gt;) and port (&lt;code&gt;3000&lt;/code&gt;), and returns "Hello World!" with a &lt;code&gt;200&lt;/code&gt; HTTP success code. Since we're listening on &lt;code&gt;localhost&lt;/code&gt;, remote clients won't be able to connect to our application.&lt;/p&gt;

&lt;p&gt;To test your application, type: &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;node hello.js
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will see the following output:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Server running at http://localhost:3000/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span class='note'&gt;&lt;strong&gt;Note:&lt;/strong&gt; Running a Node.js application in this manner will block additional commands until the application is killed by pressing &lt;code&gt;CTRL+C&lt;/code&gt;.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;To test the application, open another terminal session on your server, and connect to &lt;code&gt;localhost&lt;/code&gt; with &lt;code&gt;curl&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command second-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;curl http://localhost:&lt;span class="highlight"&gt;3000&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you see the following output, the application is working properly and listening on the correct address and port:&lt;/p&gt;
&lt;pre class="code-pre  second-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Hello World!
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you do not see the expected output, make sure that your Node.js application is running and configured to listen on the proper address and port.&lt;/p&gt;

&lt;p&gt;Once you're sure it's working, kill the application (if you haven't already) by pressing &lt;code&gt;CTRL+C&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id="step-3-—-installing-pm2"&gt;Step 3 — Installing PM2&lt;/h2&gt;

&lt;p&gt;Next let's install &lt;a href="http://pm2.keymetrics.io/"&gt;PM2&lt;/a&gt;, a process manager for Node.js applications. PM2 makes it possible to daemonize applications so that they will run in the background as a service.&lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;npm&lt;/code&gt; to install the latest version of PM2 on your server: &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo npm install pm2@latest -g
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;-g&lt;/code&gt; option tells &lt;code&gt;npm&lt;/code&gt; to install the module globally, so it's available system-wide.&lt;/p&gt;

&lt;p&gt;Let's first use the &lt;code&gt;pm2 start&lt;/code&gt; command to run your application, &lt;code&gt;hello.js&lt;/code&gt;, in the background:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;pm2 start &lt;span class="highlight"&gt;hello.js&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This also adds your application to PM2's process list, which is outputted every time you start an application:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;[PM2] Spawning PM2 daemon with pm2_home=/home/sammy/.pm2
[PM2] PM2 Successfully daemonized
[PM2] Starting /home/sammy/hello.js in fork_mode (1 instance)
[PM2] Done.
┌──────────┬────┬──────┬──────┬────────┬─────────┬────────┬─────┬───────────┬───────┬──────────┐
│ App name │ id │ mode │ pid  │ status │ restart │ uptime │ cpu │ mem       │ user  │ watching │
├──────────┼────┼──────┼──────┼────────┼─────────┼────────┼─────┼───────────┼───────┼──────────┤
│ &lt;span class="highlight"&gt;hello&lt;/span&gt;    │ 0  │ fork │ 1338 │ online │ 0       │ 0s     │ 0%  │ 23.0 MB   │ sammy │ disabled │
└──────────┴────┴──────┴──────┴────────┴─────────┴────────┴─────┴───────────┴───────┴──────────┘
 Use `pm2 show &amp;lt;id|name&amp;gt;` to get more details about an app
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, PM2 automatically assigns an &lt;code&gt;App name&lt;/code&gt; (based on the filename, without the &lt;code&gt;.js&lt;/code&gt; extension) and a PM2 &lt;code&gt;id&lt;/code&gt;. PM2 also maintains other information, such as the &lt;code&gt;PID&lt;/code&gt; of the process, its current status, and memory usage.&lt;/p&gt;

&lt;p&gt;Applications that are running under PM2 will be restarted automatically if the application crashes or is killed, but we can take an additional step to get the application to launch on system startup using the &lt;code&gt;startup&lt;/code&gt; subcommand. This subcommand generates and configures a startup script to launch PM2 and its managed processes on server boots: &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;pm2 startup systemd
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The last line of the resulting output will include a command to run with superuser privileges to set PM2 to start on boot:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;[PM2] Init System found: systemd
[PM2] To setup the Startup Script, copy/paste the following command:
&lt;span class="highlight"&gt;sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u sammy --hp /home/sammy&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Run the command from the output, with your username in place of &lt;code&gt;&lt;span class="highlight"&gt;sammy&lt;/span&gt;&lt;/code&gt;: &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u &lt;span class="highlight"&gt;sammy&lt;/span&gt; --hp /home/&lt;span class="highlight"&gt;sammy&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As an additional step, we can save the PM2 process list and corresponding environments: &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;pm2 save
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You have now created a systemd &lt;em&gt;unit&lt;/em&gt; that runs &lt;code&gt;pm2&lt;/code&gt; for your user on boot.  This &lt;code&gt;pm2&lt;/code&gt; instance, in turn, runs &lt;code&gt;hello.js&lt;/code&gt;.  &lt;/p&gt;

&lt;p&gt;Start the service with &lt;code&gt;systemctl&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl start pm2-&lt;span class="highlight"&gt;sammy&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Check the status of the systemd unit:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;systemctl status pm2-&lt;span class="highlight"&gt;sammy&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For a detailed overview of systemd, see &lt;a href="https://www.digitalocean.com/community/tutorials/systemd-essentials-working-with-services-units-and-the-journal"&gt;Systemd Essentials: Working with Services, Units, and the Journal&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In addition to those we have covered, PM2 provides many subcommands that allow you to manage or look up information about your applications.&lt;/p&gt;

&lt;p&gt;Stop an application with this command (specify the PM2 &lt;code&gt;App name&lt;/code&gt; or &lt;code&gt;id&lt;/code&gt;):&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;pm2 stop &lt;span class="highlight"&gt;app_name_or_id&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Restart an application:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;pm2 restart &lt;span class="highlight"&gt;app_name_or_id&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;List the applications currently managed by PM2:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;pm2 list
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Get information about a specific application using its &lt;code&gt;App name&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;pm2 info &lt;span class="highlight"&gt;app_name&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The PM2 process monitor can be pulled up with the &lt;code&gt;monit&lt;/code&gt; subcommand. This displays the application status, CPU, and memory usage:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;pm2 monit
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that running &lt;code&gt;pm2&lt;/code&gt; without any arguments will also display a help page with example usage.&lt;/p&gt;

&lt;p&gt;Now that your Node.js application is running and managed by PM2, let's set up the reverse proxy.&lt;/p&gt;

&lt;h2 id="step-4-—-setting-up-nginx-as-a-reverse-proxy-server"&gt;Step 4 — Setting Up Nginx as a Reverse Proxy Server&lt;/h2&gt;

&lt;p&gt;Your application is running and listening on &lt;code&gt;localhost&lt;/code&gt;, but you need to set up a way for your users to access it. We will set up the Nginx web server as a reverse proxy for this purpose.&lt;/p&gt;

&lt;p&gt;In the prerequisite tutorial, you set up your Nginx configuration in the &lt;code&gt;/etc/nginx/sites-available/&lt;span class="highlight"&gt;example.com&lt;/span&gt;&lt;/code&gt; file. Open this file for editing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/nginx/sites-available/&lt;span class="highlight"&gt;example.com&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Within the &lt;code&gt;server&lt;/code&gt; block, you should have an existing &lt;code&gt;location /&lt;/code&gt; block. Replace the contents of that block with the following configuration. If your application is set to listen on a different port, update the highlighted portion to the correct port number:&lt;/p&gt;
&lt;div class="code-label " title="/etc/nginx/sites-available/example.com"&gt;/etc/nginx/sites-available/example.com&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;server {
...
    location / {
        proxy_pass http://localhost:&lt;span class="highlight"&gt;3000&lt;/span&gt;;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This configures the server to respond to requests at its root. Assuming our server is available at &lt;code&gt;&lt;span class="highlight"&gt;example.com&lt;/span&gt;&lt;/code&gt;, accessing &lt;code&gt;https://&lt;span class="highlight"&gt;example.com&lt;/span&gt;/&lt;/code&gt; via a web browser would send the request to &lt;code&gt;hello.js&lt;/code&gt;, listening on port &lt;code&gt;3000&lt;/code&gt; at &lt;code&gt;localhost&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can add additional &lt;code&gt;location&lt;/code&gt; blocks to the same server block to provide access to other applications on the same server. For example, if you were also running another Node.js application on port &lt;code&gt;3001&lt;/code&gt;, you could add this location block to allow access to it via &lt;code&gt;https://&lt;span class="highlight"&gt;example.com&lt;/span&gt;/&lt;span class="highlight"&gt;app2&lt;/span&gt;&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-label " title="/etc/nginx/sites-available/example.com — Optional"&gt;/etc/nginx/sites-available/example.com — Optional&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;server {
...
    location /&lt;span class="highlight"&gt;app2&lt;/span&gt; {
        proxy_pass http://localhost:&lt;span class="highlight"&gt;3001&lt;/span&gt;;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once you are done adding the location blocks for your applications, save the file and exit your editor.&lt;/p&gt;

&lt;p&gt;Make sure you didn't introduce any syntax errors by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nginx -t
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Restart Nginx:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl restart nginx
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Assuming that your Node.js application is running, and your application and Nginx configurations are correct, you should now be able to access your application via the Nginx reverse proxy. Try it out by accessing your server's URL (its public IP address or domain name). &lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Congratulations! You now have your Node.js application running behind an Nginx reverse proxy on a Debian 9 server. This reverse proxy setup is flexible enough to provide your users access to other applications or static web content that you want to share. &lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/install-tomcat-9-debian-9</id>
    <published>2018-09-05T21:00:16Z</published>
    <updated>2018-09-05T21:17:08Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/install-tomcat-9-debian-9"/>
    <title>How To Install Apache Tomcat 9 on Debian 9</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;Apache Tomcat is a web server and servlet container that is used to serve Java applications. Tomcat is an open source implementation of the Java Servlet and JavaServer Pages technologies, released by the Apache Software Foundation. This tutorial covers the basic installation and some configuration of the latest release of Tomcat 9 on your Debian 9 server.&lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;Before you begin with this guide, you should have a non-root user with &lt;code&gt;sudo&lt;/code&gt; privileges set up on your server. You can learn how to do this by completing our &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-debian-9"&gt;Debian 9 initial server setup guide&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id="step-1-—-install-java"&gt;Step 1 — Install Java&lt;/h2&gt;

&lt;p&gt;Tomcat requires Java to be installed on the server so that any Java web application code can be executed. We can satisfy that requirement by installing OpenJDK with apt.&lt;/p&gt;

&lt;p&gt;First, update your apt package index:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt update
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then install the Java Development Kit package with apt:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install default-jdk
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that Java is installed, we can create a &lt;code&gt;tomcat&lt;/code&gt; user, which will be used to run the Tomcat service.&lt;/p&gt;

&lt;h2 id="step-2-—-create-tomcat-user"&gt;Step 2 — Create Tomcat User&lt;/h2&gt;

&lt;p&gt;For security purposes, Tomcat should be run as an unprivileged user (i.e. not root). We will create a new user and group that will run the Tomcat service.&lt;/p&gt;

&lt;span class='note'&gt;&lt;p&gt;
&lt;strong&gt;Note&lt;/strong&gt;: In some environments, a package called &lt;code&gt;unscd&lt;/code&gt; may be installed by default in order to speed up requests to name servers like LDAP.  The most recent version currently available in Debian contains &lt;a href="https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=844447"&gt;a bug&lt;/a&gt; that causes certain commands (like the &lt;code&gt;adduser&lt;/code&gt; command below) to produce additional output that looks like this:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;sent invalidate(passwd) request, exiting
sent invalidate(group) request, exiting
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These messages are harmless, but if you wish to avoid them, it is safe to remove the &lt;code&gt;unscd&lt;/code&gt; package if you do not not plan on using systems like LDAP for user information:&lt;/p&gt;
&lt;pre class="code-pre super_user"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="#"&gt;apt remove unscd
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;/p&gt;&lt;/span&gt;

&lt;p&gt;First, create a new &lt;code&gt;tomcat&lt;/code&gt; group:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo groupadd tomcat
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, create a new  &lt;code&gt;tomcat&lt;/code&gt; user. We'll make this user a member of the &lt;code&gt;tomcat&lt;/code&gt; group, with a home directory of &lt;code&gt;/opt/tomcat&lt;/code&gt; (where we will install Tomcat), and with a shell of &lt;code&gt;/bin/false&lt;/code&gt; (so nobody can log into the account):&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo useradd -s /bin/false -g tomcat -d /opt/tomcat tomcat
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that our &lt;code&gt;tomcat&lt;/code&gt; user is set up, let's download and install Tomcat.&lt;/p&gt;

&lt;h2 id="step-3-—-install-tomcat"&gt;Step 3 — Install Tomcat&lt;/h2&gt;

&lt;p&gt;The best way to install Tomcat 9 is to download the latest binary release then configure it manually.&lt;/p&gt;

&lt;p&gt;Find the latest version of Tomcat 9 at the &lt;a href="https://tomcat.apache.org/download-90.cgi"&gt;Tomcat 9 Downloads page&lt;/a&gt;. At the time of writing, the latest version is &lt;strong&gt;9.0.11&lt;/strong&gt;, but you should use a later stable version if it is available. Under the &lt;strong&gt;Binary Distributions&lt;/strong&gt; section, then under the &lt;strong&gt;Core&lt;/strong&gt; list, copy the link to the "tar.gz".&lt;/p&gt;

&lt;p&gt;Next, change to the &lt;code&gt;/tmp&lt;/code&gt; directory on your server.  This is a good directory to download ephemeral items, like the Tomcat tarball, which we won't need after extracting the Tomcat contents:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cd /tmp
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We’ll use the &lt;code&gt;curl&lt;/code&gt; command-line tool to download the tarball. Install &lt;code&gt;curl&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install curl
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, use &lt;code&gt;curl&lt;/code&gt; to download the link that you copied from the Tomcat website:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;curl -O &lt;span class="highlight"&gt;http://www-eu.apache.org/dist/tomcat/tomcat-9/v9.0.11/bin/apache-tomcat-9.0.11.tar.gz&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We will install Tomcat to the &lt;code&gt;/opt/tomcat&lt;/code&gt; directory. Create the directory, then extract the archive to it with these commands:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo mkdir /opt/tomcat
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo tar xzvf apache-tomcat-9*tar.gz -C /opt/tomcat --strip-components=1
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, we can set up the proper user permissions for our installation.&lt;/p&gt;

&lt;h2 id="step-4-—-update-permissions"&gt;Step 4 — Update Permissions&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;tomcat&lt;/code&gt; user that we set up needs to have access to the Tomcat installation. We'll set that up now.&lt;/p&gt;

&lt;p&gt;Change to the directory where we unpacked the Tomcat installation:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cd /opt/tomcat
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Give the &lt;code&gt;tomcat&lt;/code&gt; group ownership over the entire installation directory:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo chgrp -R tomcat /opt/tomcat
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, give the &lt;code&gt;tomcat&lt;/code&gt; group read access to the &lt;code&gt;conf&lt;/code&gt; directory and all of its contents, and &lt;strong&gt;execute&lt;/strong&gt; access to the directory itself:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo chmod -R g+r conf
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo chmod g+x conf
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Make the &lt;code&gt;tomcat&lt;/code&gt; user the owner of the &lt;code&gt;webapps&lt;/code&gt;, &lt;code&gt;work&lt;/code&gt;, &lt;code&gt;temp&lt;/code&gt;, and &lt;code&gt;logs&lt;/code&gt; directories:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo chown -R tomcat webapps/ work/ temp/ logs/
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that the proper permissions are set up, we can create a systemd service file to manage the Tomcat process.&lt;/p&gt;

&lt;h2 id="step-5-—-create-a-systemd-service-file"&gt;Step 5 — Create a systemd Service File&lt;/h2&gt;

&lt;p&gt;We want to be able to run Tomcat as a service, so we will set up systemd service file.&lt;/p&gt;

&lt;p&gt;Tomcat needs to know where Java is installed. This path is commonly referred to as "JAVA_HOME". The easiest way to look up that location is by running this command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo update-java-alternatives -l
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;java-1.8.0-openjdk-amd64       1081       &lt;span class="highlight"&gt;/usr/lib/jvm/java-1.8.0-openjdk-amd64&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Your &lt;code&gt;JAVA_HOME&lt;/code&gt; is the output from the last column (highlighted in red).  Given the example above, the correct &lt;code&gt;JAVA_HOME&lt;/code&gt; for this server would be:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="JAVA_HOME"&gt;JAVA_HOME&lt;/div&gt;/usr/lib/jvm/java-1.8.0-openjdk-amd64
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Your &lt;code&gt;JAVA_HOME&lt;/code&gt; may be different.&lt;/p&gt;

&lt;p&gt;With this piece of information, we can create the systemd service file.  Open a file called &lt;code&gt;tomcat.service&lt;/code&gt; in the &lt;code&gt;/etc/systemd/system&lt;/code&gt; directory by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/systemd/system/tomcat.service
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Paste the following contents into your service file.  Modify the value of &lt;code&gt;JAVA_HOME&lt;/code&gt; if necessary to match the value you found on your system. You may also want to modify the memory allocation settings that are specified in &lt;code&gt;CATALINA_OPTS&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-label " title="/etc/systemd/system/tomcat.service"&gt;/etc/systemd/system/tomcat.service&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;[Unit]
Description=Apache Tomcat Web Application Container
After=network.target

[Service]
Type=forking

Environment=JAVA_HOME=&lt;span class="highlight"&gt;/usr/lib/jvm/java-1.8.0-openjdk-amd64&lt;/span&gt;
Environment=CATALINA_PID=/opt/tomcat/temp/tomcat.pid
Environment=CATALINA_HOME=/opt/tomcat
Environment=CATALINA_BASE=/opt/tomcat
Environment='CATALINA_OPTS=-Xms512M -Xmx1024M -server -XX:+UseParallelGC'
Environment='JAVA_OPTS=-Djava.awt.headless=true -Djava.security.egd=file:/dev/./urandom'

ExecStart=/opt/tomcat/bin/startup.sh
ExecStop=/opt/tomcat/bin/shutdown.sh

User=tomcat
Group=tomcat
UMask=0007
RestartSec=10
Restart=always

[Install]
WantedBy=multi-user.target
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When you are finished, save and close the file.&lt;/p&gt;

&lt;p&gt;Next, reload the systemd daemon so that it knows about our service file:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl daemon-reload
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Start the Tomcat service by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl start tomcat
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Double check that it started without errors by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl status tomcat
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see output similar to the following:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;● tomcat.service - Apache Tomcat Web Application Container
   Loaded: loaded (/etc/systemd/system/tomcat.service; disabled; vendor preset: enabled)
   Active: active (running) since Wed 2018-09-05 20:47:44 UTC; 3s ago
  Process: 9037 ExecStart=/opt/tomcat/bin/startup.sh (code=exited, status=0/SUCCESS)
 Main PID: 9046 (java)
    Tasks: 46 (limit: 4915)
   CGroup: /system.slice/tomcat.service
           └─9046 /usr/lib/jvm/java-1.8.0-openjdk-amd64/bin/java -Djava.util.logging.config.file=/opt/tomcat/conf/logging.properties -Dja

Sep 05 20:47:44 tomcat systemd[1]: Starting Apache Tomcat Web Application Container...
Sep 05 20:47:44 tomcat systemd[1]: Started Apache Tomcat Web Application Container.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This confirms that Tomcat is up and running on your server.&lt;/p&gt;

&lt;h2 id="step-6-—-adjust-the-firewall-and-test-the-tomcat-server"&gt;Step 6 — Adjust the Firewall and Test the Tomcat Server&lt;/h2&gt;

&lt;p&gt;Now that the Tomcat service is started, we can test to make sure the default page is available.&lt;/p&gt;

&lt;p&gt;Before we do that, we need to adjust the firewall to allow our requests to get to the service.  If you followed the prerequisites, you will have a &lt;code&gt;ufw&lt;/code&gt; firewall enabled currently.&lt;/p&gt;

&lt;p&gt;Tomcat uses port &lt;code&gt;8080&lt;/code&gt; to accept conventional requests.  Allow traffic to that port by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw allow 8080
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With the firewall modified, you can access the default splash page by going to your domain or IP address followed by &lt;code&gt;:8080&lt;/code&gt; in a web browser:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Open in web browser"&gt;Open in web browser&lt;/div&gt;http://&lt;span class="highlight"&gt;server_domain_or_IP&lt;/span&gt;:8080
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will see the default Tomcat splash page, in addition to other information.  However, if you click the links for the Manager App, for instance, you will be denied access.  We can configure that access next.&lt;/p&gt;

&lt;p&gt;If you were able to successfully accessed Tomcat, now is a good time to enable the service file so that Tomcat automatically starts at boot:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl enable tomcat
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="step-7-—-configure-tomcat-web-management-interface"&gt;Step 7 — Configure Tomcat Web Management Interface&lt;/h2&gt;

&lt;p&gt;In order to use the manager web app that comes with Tomcat, we must add a login to our Tomcat server. We will do this by editing the &lt;code&gt;tomcat-users.xml&lt;/code&gt; file:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /opt/tomcat/conf/tomcat-users.xml
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will want to add a user who can access the &lt;code&gt;manager-gui&lt;/code&gt; and &lt;code&gt;admin-gui&lt;/code&gt; (web apps that come with Tomcat).  You can do so by defining a user, similar to the example below, between the &lt;code&gt;tomcat-users&lt;/code&gt; tags.  Be sure to change the username and password to something secure:&lt;/p&gt;
&lt;div class="code-label " title="tomcat-users.xml — Admin User"&gt;tomcat-users.xml — Admin User&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&amp;lt;tomcat-users . . .&amp;gt;
    &amp;lt;user username="&lt;span class="highlight"&gt;admin&lt;/span&gt;" password="&lt;span class="highlight"&gt;password&lt;/span&gt;" roles="manager-gui,admin-gui"/&amp;gt;
&amp;lt;/tomcat-users&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save and close the file when you are finished.&lt;/p&gt;

&lt;p&gt;By default, newer versions of Tomcat restrict access to the Manager and Host Manager apps to connections coming from the server itself.  Since we are installing on a remote machine, you will probably want to remove or alter this restriction.  To change the IP address restrictions on these, open the appropriate &lt;code&gt;context.xml&lt;/code&gt; files.&lt;/p&gt;

&lt;p&gt;For the Manager app, type:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /opt/tomcat/webapps/manager/META-INF/context.xml
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For the Host Manager app, type:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /opt/tomcat/webapps/host-manager/META-INF/context.xml
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Inside, comment out the IP address restriction to allow connections from anywhere.  Alternatively, if you would like to allow access only to connections coming from your own IP address, you can add your public IP address to the list:&lt;/p&gt;
&lt;div class="code-label " title="context.xml files for Tomcat webapps"&gt;context.xml files for Tomcat webapps&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&amp;lt;Context antiResourceLocking="false" privileged="true" &amp;gt;
  &lt;span class="highlight"&gt;&amp;lt;!--&lt;/span&gt;&amp;lt;Valve className="org.apache.catalina.valves.RemoteAddrValve"
         allow="127\.\d+\.\d+\.\d+|::1|0:0:0:0:0:0:0:1" /&amp;gt;&lt;span class="highlight"&gt;--&amp;gt;&lt;/span&gt;
&amp;lt;/Context&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save and close the files when you are finished.&lt;/p&gt;

&lt;p&gt;To put our changes into effect, restart the Tomcat service:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl restart tomcat
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="step-8-—-access-the-web-interface"&gt;Step 8 — Access the Web Interface&lt;/h2&gt;

&lt;p&gt;Now that we have create a user, we can access the web management interface again in a web browser.  Once again, you can get to the correct interface by entering your server's domain name or IP address followed on port 8080 in your browser:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Open in web browser"&gt;Open in web browser&lt;/div&gt;http://&lt;span class="highlight"&gt;server_domain_or_IP&lt;/span&gt;:8080
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The page you see should be the same one you were given when you tested earlier:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/tomcat8_1604/splashscreen.png" alt="Tomcat root"&gt;&lt;/p&gt;

&lt;p&gt;Let's take a look at the Manager App, accessible via the link or &lt;code&gt;http://&lt;span class="highlight"&gt;server_domain_or_IP&lt;/span&gt;:8080/manager/html&lt;/code&gt;.  You will need to enter the account credentials that you added to the &lt;code&gt;tomcat-users.xml&lt;/code&gt; file.  Afterwards, you should see a page that looks like this:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/tomcat8_1604/manager.png" alt="Tomcat Web Application Manager"&gt;&lt;/p&gt;

&lt;p&gt;The Web Application Manager is used to manage your Java applications. You can Start, Stop, Reload, Deploy, and Undeploy here. You can also run some diagnostics on your apps (i.e. find memory leaks). Lastly, information about your server is available at the very bottom of this page.&lt;/p&gt;

&lt;p&gt;Now let's take a look at the Host Manager, accessible via the link or &lt;code&gt;http://&lt;span class="highlight"&gt;server_domain_or_IP&lt;/span&gt;:8080/host-manager/html/&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/tomcat8_1604/host-manager.png" alt="Tomcat Virtual Host Manager"&gt;&lt;/p&gt;

&lt;p&gt;From the Virtual Host Manager page, you can add virtual hosts to serve your applications from.&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Your installation of Tomcat is complete!  Your are now free to deploy your own Java web applications!&lt;/p&gt;

&lt;p&gt;Currently, your Tomcat installation is functional, but entirely unencrypted.  This means that all data, including sensitive items like passwords, are sent in plain text that can be intercepted and read by other parties on the internet.  In order to prevent this from happening, it is strongly recommended that you encrypt your connections with SSL.  You can find out how to encrypt your connections to Tomcat by following &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-encrypt-tomcat-8-connections-with-apache-or-nginx-on-ubuntu-16-04"&gt;this guide&lt;/a&gt; (&lt;em&gt;note: this guide covers Tomcat 8 encryption on Ubuntu 16.04&lt;/em&gt;). &lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-install-and-secure-phpmyadmin-on-debian-9</id>
    <published>2018-09-05T21:01:44Z</published>
    <updated>2018-09-05T21:08:35Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-install-and-secure-phpmyadmin-on-debian-9"/>
    <title>How To Install and Secure phpMyAdmin on Debian 9</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;While many users need the functionality of a database management system like MariaDB, they may not feel comfortable interacting with the system solely from the MariaDB prompt.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.phpmyadmin.net/"&gt;phpMyAdmin&lt;/a&gt; was created so that users can interact with MariaDB through a web interface. In this guide, we'll discuss how to install and secure phpMyAdmin so that you can safely use it to manage your databases on a Debian 9 system.&lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;Before you get started with this guide, you need to have some basic steps completed.&lt;/p&gt;

&lt;p&gt;First, we'll assume that your server has a non-&lt;strong&gt;root&lt;/strong&gt; user with &lt;code&gt;sudo&lt;/code&gt; privileges, as well as a firewall configured with &lt;code&gt;ufw&lt;/code&gt;, as described in the &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-debian-9"&gt;initial server setup guide for Debian 9&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We're also going to assume that you've completed a LAMP (Linux, Apache, MariaDB, and PHP) installation on your Debian 9 server. If you’ve not yet done this, follow our guide on &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-linux-apache-mariadb-php-lamp-stack-debian9"&gt;installing a LAMP stack on Debian 9&lt;/a&gt; to set this up.&lt;/p&gt;

&lt;p&gt;Finally, there are important security considerations when using software like phpMyAdmin, since it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Communicates directly with your MariaDB installation&lt;/li&gt;
&lt;li&gt;Handles authentication using MariaDB credentials&lt;/li&gt;
&lt;li&gt;Executes and returns results for arbitrary SQL queries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For these reasons, and because it is a widely-deployed PHP application which is frequently targeted for attack, you should never run phpMyAdmin on remote systems over a plain HTTP connection. If you do not have an existing domain configured with an SSL/TLS certificate, you can follow this guide on &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-secure-apache-with-let-s-encrypt-on-debian-9"&gt;securing Apache with Let's Encrypt on Debian 9&lt;/a&gt;. This will require you to &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-point-to-digitalocean-nameservers-from-common-domain-registrars"&gt;register a domain name&lt;/a&gt;, &lt;a href="https://www.digitalocean.com/community/tutorials/an-introduction-to-digitalocean-dns"&gt;create DNS records for your server&lt;/a&gt;, and &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-the-apache-web-server-on-debian-9#step-5-%E2%80%94-setting-up-virtual-hosts-(recommended)"&gt;set up an Apache Virtual Host&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Once you are finished with these steps, you're ready to get started with this guide.&lt;/p&gt;

&lt;h2 id="step-1-—-installing-phpmyadmin"&gt;Step 1 — Installing phpMyAdmin&lt;/h2&gt;

&lt;p&gt;To get started, we will install phpMyAdmin from the default Debian repositories.&lt;/p&gt;

&lt;p&gt;This is done by updating your server’s package index and then using the &lt;code&gt;apt&lt;/code&gt; packaging system to pull down the files and install them on your system:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt update
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo apt install phpmyadmin php-mbstring php-gettext
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will ask you a few questions in order to configure your installation correctly.&lt;/p&gt;

&lt;p&gt;&lt;span class='warning'&gt;&lt;strong&gt;Warning:&lt;/strong&gt; When the prompt appears, “apache2” is highlighted, but &lt;strong&gt;not&lt;/strong&gt; selected. If you do not hit &lt;code&gt;SPACE&lt;/code&gt; to select Apache, the installer will &lt;em&gt;not&lt;/em&gt; move the necessary files during installation. Hit &lt;code&gt;SPACE&lt;/code&gt;, &lt;code&gt;TAB&lt;/code&gt;, and then &lt;code&gt;ENTER&lt;/code&gt; to select Apache.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For the server selection, choose &lt;code&gt;apache2&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Select &lt;code&gt;Yes&lt;/code&gt; when asked whether to use &lt;code&gt;dbconfig-common&lt;/code&gt; to set up the database&lt;/li&gt;
&lt;li&gt;You will then be asked to choose and confirm a MySQL application password for phpMyAdmin&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;span class='note'&gt;&lt;strong&gt;Note:&lt;/strong&gt; MariaDB is a community-developed fork of MySQL, and although the two programs are closely related, they are not completely interchangeable. While phpMyAdmin was designed specifically for managing MySQL databases and makes reference to MySQL in various dialogue boxes, rest assured that your installation of MariaDB will work correctly with phpMyAdmin.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;The installation process adds the phpMyAdmin Apache configuration file into the &lt;code&gt;/etc/apache2/conf-enabled/&lt;/code&gt; directory, where it is read automatically. The only thing you need to do is explicitly enable the &lt;code&gt;mbstring&lt;/code&gt; PHP extension which is used to manage non-ASCII strings and convert strings to different encodings. Do this by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo phpenmod mbstring
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Afterwards, restart Apache for your changes to be recognized:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl restart apache2
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;phpMyAdmin is now installed and configured. However, before you can log in and begin managing your MariaDB databases, you will need to ensure that your MariaDB users have the privileges required for interacting with the program.&lt;/p&gt;

&lt;h2 id="step-2-—-adjusting-user-authentication-and-privileges"&gt;Step 2 — Adjusting User Authentication and Privileges&lt;/h2&gt;

&lt;p&gt;When you installed phpMyAdmin onto your server, it automatically created a database user called &lt;code&gt;phpmyadmin&lt;/code&gt; which performs certain underlying processes for the program. Rather than logging in as this user with the administrative password you set during installation, it’s recommended that you log in using a different account.&lt;/p&gt;

&lt;p&gt;In new installs on Debian systems, the &lt;strong&gt;root&lt;/strong&gt; MariaDB user is set to authenticate using the &lt;code&gt;unix_socket&lt;/code&gt; plugin by default rather than with a password. This allows for some greater security and usability in many cases, but it can also complicate things when you need to allow an external program (e.g., phpMyAdmin) administrative rights through this user. Because the server uses the &lt;strong&gt;root&lt;/strong&gt; account for tasks like log rotation and starting and stopping the server, it is best not to change the &lt;strong&gt;root&lt;/strong&gt; account's authentication details. Since phpMyAdmin requires users to authenticate with a password, you will need to create a new MariaDB account in order to access the interface.&lt;/p&gt;

&lt;p&gt;If you followed the prerequisite tutorial on &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-linux-apache-mariadb-php-lamp-stack-debian9#step-2-%E2%80%94-installing-mariadb"&gt;installing a LAMP stack&lt;/a&gt; and created a MariaDB user account as described in Step 2, you can just log in to phpMyAdmin under that account using the password you created when you set it up by visiting this link:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;https://&lt;span class="highlight"&gt;your_domain_or_IP&lt;/span&gt;/phpmyadmin
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you haven’t created a MariaDB user, or if you have but you’d like to create another user just for the purpose of managing databases through phpMyAdmin, continue on with this section to learn how to set one up.&lt;/p&gt;

&lt;p&gt;Begin by opening up the MariaDB shell:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo mariadb
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;span class='note'&gt;&lt;p&gt;
&lt;strong&gt;Note:&lt;/strong&gt; If you have password authentication enabled, as you would if you’ve already created a new user account for your MariaDB server, you will need to use a different command to access the MariaDB shell. The following will run your MariaDB client with regular user privileges, and you will only gain administrator privileges within the database by authenticating:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;mariadb -u &lt;span class="highlight"&gt;user&lt;/span&gt; -p
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;/p&gt;&lt;/span&gt;

&lt;p&gt;From there, create a new user and give it a strong password:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="MariaDB [(none)]&amp;gt;"&gt;CREATE USER '&lt;span class="highlight"&gt;sammy&lt;/span&gt;'@'localhost' IDENTIFIED BY '&lt;span class="highlight"&gt;password&lt;/span&gt;';
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, grant your new user appropriate privileges. For example, you could grant the user privileges to all tables within the database, as well as the power to add, change, and remove user privileges, with this command:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="MariaDB [(none)]&amp;gt;"&gt;GRANT ALL PRIVILEGES ON *.* TO '&lt;span class="highlight"&gt;sammy&lt;/span&gt;'@'localhost' WITH GRANT OPTION;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Following that, exit the MariaDB shell:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="MariaDB [(none)]&amp;gt;"&gt;exit
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can now access the web interface by visiting your server's domain name or public IP address, followed by &lt;code&gt;/phpmyadmin&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;https://&lt;span class="highlight"&gt;your_domain_or_IP&lt;/span&gt;/phpmyadmin
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/phpmyadmin_1804/phpmyadmin_sammy_login_small.png" alt="phpMyAdmin login screen"&gt;&lt;/p&gt;

&lt;p&gt;Log in to the interface with the username and password you configured.&lt;/p&gt;

&lt;p&gt;When you log in, you'll see the user interface, which will look something like this:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/phpmyadmin_deb9/deb9_phpmyadmin_ui.png" alt="phpMyAdmin user interface"&gt;&lt;/p&gt;

&lt;p&gt;Now that you’re able to connect and interact with phpMyAdmin, all that’s left to do is harden your system’s security to protect it from attackers.&lt;/p&gt;

&lt;h2 id="step-3-—-securing-your-phpmyadmin-instance"&gt;Step 3 — Securing Your phpMyAdmin Instance&lt;/h2&gt;

&lt;p&gt;Because of its ubiquity, phpMyAdmin is a popular target for attackers, and you should take extra care to prevent unauthorized access. One of the easiest ways of doing this is to place a gateway in front of the entire application by using Apache's built-in &lt;code&gt;.htaccess&lt;/code&gt; authentication and authorization functionalities.&lt;/p&gt;

&lt;p&gt;To do this, you must first enable the use of &lt;code&gt;.htaccess&lt;/code&gt; file overrides by editing your Apache configuration file.&lt;/p&gt;

&lt;p&gt;Edit the linked file that has been placed in your Apache configuration directory:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/apache2/conf-available/phpmyadmin.conf
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add an &lt;code&gt;AllowOverride All&lt;/code&gt; directive within the &lt;code&gt;&amp;lt;Directory /usr/share/phpmyadmin&amp;gt;&lt;/code&gt; section of the configuration file, like this:&lt;/p&gt;
&lt;div class="code-label " title="/etc/apache2/conf-available/phpmyadmin.conf"&gt;/etc/apache2/conf-available/phpmyadmin.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&amp;lt;Directory /usr/share/phpmyadmin&amp;gt;
    Options FollowSymLinks
    DirectoryIndex index.php
    &lt;span class="highlight"&gt;AllowOverride All&lt;/span&gt;
    . . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When you have added this line, save and close the file.&lt;/p&gt;

&lt;p&gt;To implement the changes you made, restart Apache:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl restart apache2
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that you have enabled &lt;code&gt;.htaccess&lt;/code&gt; use for your application, you need to create one to actually implement some security.&lt;/p&gt;

&lt;p&gt;In order for this to be successful, the file must be created within the application directory. You can create the necessary file and open it in your text editor with root privileges by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /usr/share/phpmyadmin/.htaccess
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Within this file, enter the following information:&lt;/p&gt;
&lt;div class="code-label " title="/usr/share/phpmyadmin/.htaccess"&gt;/usr/share/phpmyadmin/.htaccess&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;span class="highlight"&gt;AuthType Basic&lt;/span&gt;
&lt;span class="highlight"&gt;AuthName "Restricted Files"&lt;/span&gt;
&lt;span class="highlight"&gt;AuthUserFile /etc/phpmyadmin/.htpasswd&lt;/span&gt;
&lt;span class="highlight"&gt;Require valid-user&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here is what each of these lines mean:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;AuthType Basic&lt;/code&gt;: This line specifies the authentication type that you are implementing. This type will implement password authentication using a password file.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AuthName&lt;/code&gt;: This sets the message for the authentication dialog box. You should keep this generic so that unauthorized users won't gain any information about what is being protected.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AuthUserFile&lt;/code&gt;: This sets the location of the password file that will be used for authentication. This should be outside of the directories that are being served. We will create this file shortly.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Require valid-user&lt;/code&gt;: This specifies that only authenticated users should be given access to this resource. This is what actually stops unauthorized users from entering.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When you are finished, save and close the file.&lt;/p&gt;

&lt;p&gt;The location that you selected for your password file was &lt;code&gt;/etc/phpmyadmin/.htpasswd&lt;/code&gt;. You can now create this file and pass it an initial user with the &lt;code&gt;htpasswd&lt;/code&gt; utility:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo htpasswd -c /etc/phpmyadmin/.htpasswd &lt;span class="highlight"&gt;username&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will be prompted to select and confirm a password for the user you are creating. Afterwards, the file is created with the hashed password that you entered.&lt;/p&gt;

&lt;p&gt;If you want to enter an additional user, you need to do so &lt;strong&gt;without&lt;/strong&gt; the &lt;code&gt;-c&lt;/code&gt; flag, like this:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo htpasswd /etc/phpmyadmin/.htpasswd &lt;span class="highlight"&gt;additionaluser&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, when you access your phpMyAdmin subdirectory, you will be prompted for the additional account name and password that you just configured:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;https://&lt;span class="highlight"&gt;domain_name_or_IP&lt;/span&gt;/phpmyadmin
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/phpmyadmin_1404/apache_auth.png" alt="phpMyAdmin apache password"&gt;&lt;/p&gt;

&lt;p&gt;After entering the Apache authentication, you'll be taken to the regular phpMyAdmin authentication page to enter your MariaDB credentials. This setup adds an additional layer of security, which is desirable since phpMyAdmin has suffered from vulnerabilities in the past.&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;You should now have phpMyAdmin configured and ready to use on your Debian 9 server. Using this interface, you can easily create databases, users, tables, etc., and perform the usual operations like deleting and modifying structures and data.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-secure-apache-with-let-s-encrypt-on-debian-9</id>
    <published>2018-09-05T20:42:04Z</published>
    <updated>2018-09-05T20:56:25Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-secure-apache-with-let-s-encrypt-on-debian-9"/>
    <title>How To Secure Apache with Let's Encrypt on Debian 9</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;Let's Encrypt is a Certificate Authority (CA) that provides an easy way to obtain and install free &lt;a href="https://www.digitalocean.com/community/tutorials/openssl-essentials-working-with-ssl-certificates-private-keys-and-csrs"&gt;TLS/SSL certificates&lt;/a&gt;, thereby enabling encrypted HTTPS on web servers. It simplifies the process by providing a software client, Certbot, that attempts to automate most (if not all) of the required steps. Currently, the entire process of obtaining and installing a certificate is fully automated on both Apache and Nginx.&lt;/p&gt;

&lt;p&gt;In this tutorial, you will use Certbot to obtain a free SSL certificate for Apache on Debian 9 and set up your certificate to renew automatically.&lt;/p&gt;

&lt;p&gt;This tutorial will use a separate Apache virtual host file instead of the default configuration file. &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-the-apache-web-server-on-debian-9#step-5-%E2%80%94-setting-up-virtual-hosts-(recommended)"&gt;We recommend&lt;/a&gt; creating new Apache virtual host files for each domain because it helps to avoid common mistakes and maintains the default files as a fallback configuration. &lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;To follow this tutorial, you will need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;One Debian 9 server set up by following this &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-debian-9"&gt;initial server setup for Debian 9&lt;/a&gt; tutorial, including a non-&lt;strong&gt;root&lt;/strong&gt; user with &lt;code&gt;sudo&lt;/code&gt; privileges and a firewall.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A fully registered domain name. This tutorial will use &lt;strong&gt;example.com&lt;/strong&gt; throughout. You can purchase a domain name on &lt;a href="https://namecheap.com"&gt;Namecheap&lt;/a&gt;, get one for free on &lt;a href="http://www.freenom.com/en/index.html"&gt;Freenom&lt;/a&gt;, or use the domain registrar of your choice.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Both of the following DNS records set up for your server. You can follow &lt;a href="https://www.digitalocean.com/community/tutorials/an-introduction-to-digitalocean-dns"&gt;this introduction to DigitalOcean DNS&lt;/a&gt; for details on how to add them.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An A record with &lt;code&gt;&lt;span class="highlight"&gt;example.com&lt;/span&gt;&lt;/code&gt; pointing to your server's public IP address.&lt;/li&gt;
&lt;li&gt;An A record with &lt;code&gt;www.&lt;span class="highlight"&gt;example.com&lt;/span&gt;&lt;/code&gt; pointing to your server's public IP address.&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Apache installed by following &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-the-apache-web-server-on-debian-9"&gt;How To Install Apache on Debian 9&lt;/a&gt;. Be sure that you have a &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-the-apache-web-server-on-debian-9#step-5-%E2%80%94-setting-up-virtual-hosts-(recommended)"&gt;virtual host file&lt;/a&gt; for your domain. This tutorial will use &lt;code&gt;/etc/apache2/sites-available/&lt;span class="highlight"&gt;example.com&lt;/span&gt;.conf&lt;/code&gt; as an example.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="step-1-—-installing-certbot"&gt;Step 1 — Installing Certbot&lt;/h2&gt;

&lt;p&gt;The first step to using Let's Encrypt to obtain an SSL certificate is to install the Certbot software on your server.&lt;/p&gt;

&lt;p&gt;As of this writing, Certbot is not available from the Debian software repositories by default. In order to download the software using &lt;code&gt;apt&lt;/code&gt;, you will need to add the backports repository to your &lt;code&gt;sources.list&lt;/code&gt; file where &lt;code&gt;apt&lt;/code&gt; looks for package sources. Backports are packages from Debian’s testing and unstable distributions that are recompiled so they will run without new libraries on stable Debian distributions. &lt;/p&gt;

&lt;p&gt;To add the backports repository, open (or create) the &lt;code&gt;sources.list&lt;/code&gt; file in your &lt;code&gt;/etc/apt/&lt;/code&gt; directory:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/apt/sources.list
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At the bottom of the file, add the following line:&lt;/p&gt;
&lt;div class="code-label " title="/etc/apt/sources.list.d/sources.list"&gt;/etc/apt/sources.list.d/sources.list&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .
deb http://ftp.debian.org/debian stretch-backports main
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This includes the &lt;code&gt;main&lt;/code&gt; packages, which are &lt;a href="https://www.debian.org/social_contract#guidelines"&gt;Debian Free Software Guidelines (DFSG)&lt;/a&gt;-compliant, as well as the &lt;code&gt;non-free&lt;/code&gt; and &lt;code&gt;contrib&lt;/code&gt; components, which are either not DFSG-compliant themselves or include dependencies in this category.&lt;/p&gt;

&lt;p&gt;Save and close the file by pressing &lt;code&gt;CTRL+X&lt;/code&gt;, &lt;code&gt;Y&lt;/code&gt;, then &lt;code&gt;ENTER&lt;/code&gt;, then update your package lists:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt update
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then install Certbot with the following command. Note that the &lt;code&gt;-t&lt;/code&gt; option tells &lt;code&gt;apt&lt;/code&gt; to search for the package by looking in the backports repository you just added:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install python-certbot-apache -t stretch-backports
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Certbot is now ready to use, but in order for it to configure SSL for Apache, we need to verify that Apache has been configured correctly.&lt;/p&gt;

&lt;h2 id="step-2-—-setting-up-the-ssl-certificate"&gt;Step 2 — Setting Up the SSL Certificate&lt;/h2&gt;

&lt;p&gt;Certbot needs to be able to find the correct virtual host in your Apache configuration for it to automatically configure SSL. Specifically, it does this by looking for a &lt;code&gt;ServerName&lt;/code&gt; directive that matches the domain you request a certificate for.&lt;/p&gt;

&lt;p&gt;If you followed the &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-the-apache-web-server-on-debian-9#step-5-%E2%80%94-setting-up-virtual-hosts-(recommended)"&gt;virtual host set up step in the Apache installation tutorial&lt;/a&gt;, you should have a &lt;code&gt;VirtualHost&lt;/code&gt; block for your domain at &lt;code&gt;/etc/apache2/sites-available/&lt;span class="highlight"&gt;example.com&lt;/span&gt;.conf&lt;/code&gt; with the &lt;code&gt;ServerName&lt;/code&gt; directive already set appropriately.&lt;/p&gt;

&lt;p&gt;To check, open the virtual host file for your domain using &lt;code&gt;nano&lt;/code&gt; or your favorite text editor:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/apache2/sites-available/&lt;span class="highlight"&gt;example.com&lt;/span&gt;.conf
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Find the existing &lt;code&gt;ServerName&lt;/code&gt; line. It should look like this, with your own domain name instead of &lt;code&gt;&lt;span class="highlight"&gt;example.com&lt;/span&gt;&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-label " title="/etc/apache2/sites-available/example.com.conf"&gt;/etc/apache2/sites-available/example.com.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-apache"&gt;...
ServerName &lt;span class="highlight"&gt;example.com&lt;/span&gt;;
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If it doesn’t already, update the &lt;code&gt;ServerName&lt;/code&gt; directive to point to your domain name. Then save the file, quit your editor, and verify the syntax of your configuration edits:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apache2ctl configtest
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If there aren't any syntax errors, you will see this output:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Syntax OK
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you get an error, reopen the virtual host file and check for any typos or missing characters. Once your configuration file's syntax is correct, reload Apache to load the new configuration:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl reload apache2
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Certbot can now find the correct VirtualHost block and update it.&lt;/p&gt;

&lt;p&gt;Next, let's update the firewall to allow HTTPS traffic.&lt;/p&gt;

&lt;h2 id="step-3-—-allowing-https-through-the-firewall"&gt;Step 3 — Allowing HTTPS Through the Firewall&lt;/h2&gt;

&lt;p&gt;If you have the &lt;code&gt;ufw&lt;/code&gt; firewall enabled, as recommended by the prerequisite guides, you'll need to adjust the settings to allow for HTTPS traffic. Luckily, when installed on Debian, &lt;code&gt;ufw&lt;/code&gt; comes packaged with a few profiles that help to simplify the process of changing firewall rules for HTTP and HTTPS traffic.&lt;/p&gt;

&lt;p&gt;You can see the current setting by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw status
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you followed the Step 2 of our guide on &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-the-apache-web-server-on-debian-9#step-2-%E2%80%94-adjusting-the-firewall"&gt;How to Install Apache on Debian 9&lt;/a&gt;, the output of this command will look like this, showing that only HTTP traffic is allowed to the web server:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Status: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere                  
WWW                        ALLOW       Anywhere                  
OpenSSH (v6)               ALLOW       Anywhere (v6)             
WWW (v6)                   ALLOW       Anywhere (v6)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To additionally let in HTTPS traffic, allow the “WWW Full” profile and delete the redundant “WWW” profile allowance:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw allow 'WWW Full'
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo ufw delete allow 'WWW'
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Your status should now look like this:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw status
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Status: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere                  
WWW Full                   ALLOW       Anywhere                  
OpenSSH (v6)               ALLOW       Anywhere (v6)             
WWW Full (v6)              ALLOW       Anywhere (v6)        
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, let's run Certbot and fetch our certificates.&lt;/p&gt;

&lt;h2 id="step-4-—-obtaining-an-ssl-certificate"&gt;Step 4 — Obtaining an SSL Certificate&lt;/h2&gt;

&lt;p&gt;Certbot provides a variety of ways to obtain SSL certificates through plugins. The Apache plugin will take care of reconfiguring Apache and reloading the config whenever necessary. To use this plugin, type the following:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo certbot --apache -d &lt;span class="highlight"&gt;example.com&lt;/span&gt; -d &lt;span class="highlight"&gt;www.example.com&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This runs &lt;code&gt;certbot&lt;/code&gt; with the &lt;code&gt;--apache&lt;/code&gt; plugin, using &lt;code&gt;-d&lt;/code&gt; to specify the names you'd like the certificate to be valid for.&lt;/p&gt;

&lt;p&gt;If this is your first time running &lt;code&gt;certbot&lt;/code&gt;, you will be prompted to enter an email address and agree to the terms of service. After doing so, &lt;code&gt;certbot&lt;/code&gt; will communicate with the Let's Encrypt server, then run a challenge to verify that you control the domain you're requesting a certificate for.&lt;/p&gt;

&lt;p&gt;If that's successful, &lt;code&gt;certbot&lt;/code&gt; will ask how you'd like to configure your HTTPS settings:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Please choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access.
-------------------------------------------------------------------------------
1: No redirect - Make no further changes to the webserver configuration.
2: Redirect - Make all requests redirect to secure HTTPS access. Choose this for
new sites, or if you're confident your site works on HTTPS. You can undo this
change by editing your web server's configuration.
-------------------------------------------------------------------------------
Select the appropriate number [1-2] then [enter] (press 'c' to cancel):
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Select your choice then hit &lt;code&gt;ENTER&lt;/code&gt;. The configuration will be updated, and Apache will reload to pick up the new settings. &lt;code&gt;certbot&lt;/code&gt; will wrap up with a message telling you the process was successful and where your certificates are stored:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/&lt;span class="highlight"&gt;example.com&lt;/span&gt;/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/&lt;span class="highlight"&gt;example.com&lt;/span&gt;/privkey.pem
   Your cert will expire on 2018-12-04. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot again
   with the "certonly" option. To non-interactively renew *all* of
   your certificates, run "certbot renew"
 - Your account credentials have been saved in your Certbot
   configuration directory at /etc/letsencrypt. You should make a
   secure backup of this folder now. This configuration directory will
   also contain certificates and private keys obtained by Certbot so
   making regular backups of this folder is ideal.
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Your certificates are downloaded, installed, and loaded. Try reloading your website using &lt;code&gt;https://&lt;/code&gt; and notice your browser's security indicator. It should indicate that the site is properly secured, usually with a green lock icon. If you test your server using the &lt;a href="https://www.ssllabs.com/ssltest/"&gt;SSL Labs Server Test&lt;/a&gt;, it will get an &lt;strong&gt;A&lt;/strong&gt; grade.&lt;/p&gt;

&lt;p&gt;Let's finish by testing the renewal process.&lt;/p&gt;

&lt;h2 id="step-5-—-verifying-certbot-auto-renewal"&gt;Step 5 — Verifying Certbot Auto-Renewal&lt;/h2&gt;

&lt;p&gt;Let's Encrypt's certificates are only valid for ninety days. This is to encourage users to automate their certificate renewal process. The &lt;code&gt;certbot&lt;/code&gt; package we installed takes care of this for us by adding a renew script to &lt;code&gt;/etc/cron.d&lt;/code&gt;. This script runs twice a day and will automatically renew any certificate that's within thirty days of expiration.&lt;/p&gt;

&lt;p&gt;To test the renewal process, you can do a dry run with &lt;code&gt;certbot&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo certbot renew --dry-run
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you see no errors, you're all set. When necessary, Certbot will renew your certificates and reload Apache to pick up the changes. If the automated renewal process ever fails, Let’s Encrypt will send a message to the email you specified, warning you when your certificate is about to expire.&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;In this tutorial, you installed the Let's Encrypt client &lt;code&gt;certbot&lt;/code&gt;, downloaded SSL certificates for your domain, configured Apache to use these certificates, and set up automatic certificate renewal. If you have further questions about using Certbot, &lt;a href="https://certbot.eff.org/docs/"&gt;their documentation&lt;/a&gt; is a good place to start.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-install-and-secure-redis-on-debian-9</id>
    <published>2018-09-05T20:33:58Z</published>
    <updated>2018-09-05T20:34:48Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-install-and-secure-redis-on-debian-9"/>
    <title>How To Install and Secure Redis on Debian 9</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://redis.io/"&gt;Redis&lt;/a&gt; is an in-memory key-value store known for its flexibility, performance, and wide language support. This tutorial demonstrates how to install, configure, and secure Redis on a Debian 9 server.&lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;To complete this guide, you will need access to a Debian 9 server that has a non-root user with &lt;code&gt;sudo&lt;/code&gt; privileges and a basic firewall configured. You can set this up by following our &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-debian-9"&gt;Initial Server Setup guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;When you are ready to begin, log in to your server as your sudo-enabled user and continue below.&lt;/p&gt;

&lt;h2 id="step-1-—-installing-and-configuring-redis"&gt;Step 1 — Installing and Configuring Redis&lt;/h2&gt;

&lt;p&gt;In order to get the latest version of Redis, we will use &lt;code&gt;apt&lt;/code&gt; to install it from the official Debian repositories.&lt;/p&gt;

&lt;p&gt;Update your local &lt;code&gt;apt&lt;/code&gt; package cache and install Redis by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt update
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo apt install redis-server
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will download and install Redis and its dependencies. Following this, there is one important configuration change to make in the Redis configuration file, which was generated automatically during the installation.&lt;/p&gt;

&lt;p&gt;Open this file with your preferred text editor:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/redis/redis.conf
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Inside the file, find the &lt;code&gt;supervised&lt;/code&gt; directive. This directive allows you to declare an init system to manage Redis as a service, providing you with more control over its operation. The &lt;code&gt;supervised&lt;/code&gt; directive is set to &lt;code&gt;no&lt;/code&gt; by default. Since you are running Debian, which uses the systemd init system, change this to &lt;code&gt;systemd&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-label " title="/etc/redis/redis.conf"&gt;/etc/redis/redis.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .

# If you run Redis from upstart or systemd, Redis can interact with your
# supervision tree. Options:
#   supervised no      - no supervision interaction
#   supervised upstart - signal upstart by putting Redis into SIGSTOP mode
#   supervised systemd - signal systemd by writing READY=1 to $NOTIFY_SOCKET
#   supervised auto    - detect upstart or systemd method based on
#                        UPSTART_JOB or NOTIFY_SOCKET environment variables
# Note: these supervision methods only signal "process is ready."
#       They do not enable continuous liveness pings back to your supervisor.
supervised &lt;span class="highlight"&gt;systemd&lt;/span&gt;

. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That’s the only change you need to make to the Redis configuration file at this point, so save and close it when you are finished. Then, reload the Redis service file to reflect the changes you made to the configuration file:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl restart redis
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With that, you’ve installed and configured Redis and it’s running on your machine. Before you begin using it, though, it’s prudent to first check whether Redis is functioning correctly.&lt;/p&gt;

&lt;h2 id="step-2-—-testing-redis"&gt;Step 2 — Testing Redis&lt;/h2&gt;

&lt;p&gt;As with any newly-installed software, it’s a good idea to ensure that Redis is functioning as expected before making any further changes to its configuration. We will go over a handful of ways to check that Redis is working correctly in this step.&lt;/p&gt;

&lt;p&gt;Start by checking that the Redis service is running:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl status redis
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If it is running without any errors, this command will produce output similar to the following:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;● redis-server.service - Advanced key-value store
   Loaded: loaded (/lib/systemd/system/redis-server.service; &lt;span class="highlight"&gt;enabled&lt;/span&gt;; vendor preset: enabled)
   Active: &lt;span class="highlight"&gt;active (running)&lt;/span&gt; since Wed 2018-09-05 20:19:44 UTC; 41s ago
     Docs: http://redis.io/documentation,
           man:redis-server(1)
  Process: 10829 ExecStopPost=/bin/run-parts --verbose /etc/redis/redis-server.post-down.d (code=exited, status=0/SUCCESS)
  Process: 10825 ExecStop=/bin/kill -s TERM $MAINPID (code=exited, status=0/SUCCESS)
  Process: 10823 ExecStop=/bin/run-parts --verbose /etc/redis/redis-server.pre-down.d (code=exited, status=0/SUCCESS)
  Process: 10842 ExecStartPost=/bin/run-parts --verbose /etc/redis/redis-server.post-up.d (code=exited, status=0/SUCCESS)
  Process: 10838 ExecStart=/usr/bin/redis-server /etc/redis/redis.conf (code=exited, status=0/SUCCESS)
  Process: 10834 ExecStartPre=/bin/run-parts --verbose /etc/redis/redis-server.pre-up.d (code=exited, status=0/SUCCESS)
 Main PID: 10841 (redis-server)
    Tasks: 3 (limit: 4915)
   CGroup: /system.slice/redis-server.service
           └─10841 /usr/bin/redis-server 127.0.0.1:6379
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, you can see that Redis is running and is already enabled, meaning that it is set to start up every time the server boots.&lt;/p&gt;

&lt;span class='note'&gt;&lt;p&gt;
&lt;strong&gt;Note:&lt;/strong&gt; This setting is desirable for many common use cases of Redis. If, however, you prefer to start up Redis manually every time your server boots, you can configure this with the following command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl disable redis
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;/p&gt;&lt;/span&gt;

&lt;p&gt;To test that Redis is functioning correctly, connect to the server using the command-line client:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;redis-cli
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the prompt that follows, test connectivity with the &lt;code&gt;ping&lt;/code&gt; command:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="127.0.0.1:6379&amp;gt;"&gt;ping
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;PONG
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This output confirms that the server connection is still alive. Next, check that you’re able to set keys by running:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="127.0.0.1:6379&amp;gt;"&gt;set test "It's working!"
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;OK
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Retrieve the value by typing:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="127.0.0.1:6379&amp;gt;"&gt;get test
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Assuming everything is working, you will be able to retrieve the value you stored:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;"It's working!"
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After confirming that you can fetch the value, exit the Redis prompt to get back to the shell:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="127.0.0.1:6379&amp;gt;"&gt;exit
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As a final test, we will check whether Redis is able to persist data even after it’s been stopped or restarted. To do this, first restart the Redis instance:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl restart redis
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then connect with the command-line client once again and confirm that your test value is still available:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;redis-cli
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="127.0.0.1:6379&amp;gt;"&gt;get test
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The value of your key should still be accessible:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;"It's working!"
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Exit out into the shell again when you are finished:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="127.0.0.1:6379&amp;gt;"&gt;exit
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With that, your Redis installation is fully operational and ready for you to use. However, some of its default configuration settings are insecure and provide malicious actors with opportunities to attack and gain access to your server and its data. The remaining steps in this tutorial cover methods for mitigating these vulnerabilities, as prescribed by the &lt;a href="http://redis.io/topics/security"&gt;official Redis website&lt;/a&gt;. Although these steps are optional and Redis will still function if you choose not to follow them, it is &lt;em&gt;strongly&lt;/em&gt; recommended that you complete them in order to harden your system’s security.&lt;/p&gt;

&lt;h2 id="step-3-—-binding-to-localhost"&gt;Step 3 — Binding to localhost&lt;/h2&gt;

&lt;p&gt;By default, Redis is only accessible from &lt;strong&gt;localhost&lt;/strong&gt;. However, if you installed and configured Redis by following a different tutorial than this one, you might have updated the configuration file to allow connections from anywhere. This is not as secure as binding to &lt;strong&gt;localhost&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;To correct this, open the Redis configuration file for editing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/redis/redis.conf
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Locate this line and make sure it is uncommented (remove the &lt;code&gt;#&lt;/code&gt; if it exists):&lt;/p&gt;
&lt;div class="code-label " title="/etc/redis/redis.conf"&gt;/etc/redis/redis.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;bind 127.0.0.1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save and close the file when finished (press &lt;code&gt;CTRL + X&lt;/code&gt;, &lt;code&gt;Y&lt;/code&gt;, then &lt;code&gt;ENTER&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Then, restart the service to ensure that systemd reads your changes:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl restart redis
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To check that this change has gone into effect, run the following &lt;code&gt;netstat&lt;/code&gt; command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo netstat -lnp | grep redis
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;tcp        0      0 127.0.0.1:6379          0.0.0.0:*               LISTEN      10959/redis-server
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This output shows that the &lt;code&gt;redis-server&lt;/code&gt; program is bound to &lt;strong&gt;localhost&lt;/strong&gt; (&lt;code&gt;127.0.0.1&lt;/code&gt;), reflecting the change you just made to the configuration file. If you see another IP address in that column (&lt;code&gt;0.0.0.0&lt;/code&gt;, for example), then you should double check that you uncommented the correct line and restart the Redis service again.&lt;/p&gt;

&lt;p&gt;Now that your Redis installation is only listening in on &lt;strong&gt;localhost&lt;/strong&gt;, it will be more difficult for malicious actors to make requests or gain access to your server. However, Redis isn’t currently set to require users to authenticate themselves before making changes to its configuration or the data it holds. To remedy this, Redis allows you to require users to authenticate with a password before making changes via the Redis client (&lt;code&gt;redis-cli&lt;/code&gt;).&lt;/p&gt;

&lt;h2 id="step-4-—-configuring-a-redis-password"&gt;Step 4 — Configuring a Redis Password&lt;/h2&gt;

&lt;p&gt;Configuring a Redis password enables one of its two built-in security features — the &lt;code&gt;auth&lt;/code&gt; command, which requires clients to authenticate to access the database. The password is configured directly in Redis's configuration file, &lt;code&gt;/etc/redis/redis.conf&lt;/code&gt;, so open that file again with your preferred editor:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/redis/redis.conf
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Scroll to the &lt;code&gt;SECURITY&lt;/code&gt; section and look for a commented directive that reads:&lt;/p&gt;
&lt;div class="code-label " title="/etc/redis/redis.conf"&gt;/etc/redis/redis.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;# requirepass foobared
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Uncomment it by removing the &lt;code&gt;#&lt;/code&gt;, and change &lt;code&gt;foobared&lt;/code&gt; to a secure password.&lt;/p&gt;

&lt;span class='note'&gt;&lt;p&gt;
&lt;strong&gt;Note:&lt;/strong&gt; Above the &lt;code&gt;requirepass&lt;/code&gt; directive in the &lt;code&gt;redis.conf&lt;/code&gt; file, there is a commented warning:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;# Warning: since Redis is pretty fast an outside user can try up to
# 150k passwords per second against a good box. This means that you should
# use a very strong password otherwise it will be very easy to break.
#
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Thus, it’s important that you specify a very strong and very long value as your password. Rather than make up a password yourself, you can use the &lt;code&gt;openssl&lt;/code&gt; command to generate a random one, as in the following example. By piping the output of the first command to the second &lt;code&gt;openssl&lt;/code&gt; command, as shown here, it will remove any line breaks produced by that the first command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;openssl rand 60 | openssl base64 -A
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Your output should look something like:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;RBOJ9cCNoGCKhlEBwQLHri1g+atWgn4Xn4HwNUbtzoVxAYxkiYBi7aufl4MILv1nxBqR4L6NNzI0X6cE
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After copying and pasting the output of that command as the new value for &lt;code&gt;requirepass&lt;/code&gt;, it should read:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="/etc/redis/redis.conf"&gt;/etc/redis/redis.conf&lt;/div&gt;requirepass &lt;span class="highlight"&gt;RBOJ9cCNoGCKhlEBwQLHri1g+atWgn4Xn4HwNUbtzoVxAYxkiYBi7aufl4MILv1nxBqR4L6NNzI0X6cE&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;/p&gt;&lt;/span&gt;

&lt;p&gt;After setting the password, save and close the file, then restart Redis:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl restart redis.service
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To test that the password works, access the Redis command line:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;redis-cli
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The following shows a sequence of commands used to test whether the Redis password works. The first command tries to set a key to a value before authentication:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="127.0.0.1:6379&amp;gt;"&gt;set key1 10
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That won't work because you didn’t authenticate, so Redis returns an error:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;(error) NOAUTH Authentication required.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The next command authenticates with the password specified in the Redis configuration file:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="127.0.0.1:6379&amp;gt;"&gt;auth &lt;span class="highlight"&gt;your_redis_password&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Redis acknowledges:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;OK
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After that, running the previous command again will succeed:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="127.0.0.1:6379&amp;gt;"&gt;set key1 10
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;OK
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;get key1&lt;/code&gt; queries Redis for the value of the new key.&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="127.0.0.1:6379&amp;gt;"&gt;get key1
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;"10"
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After confirming that you’re able to run commands in the Redis client after authenticating, you can exit the &lt;code&gt;redis-cli&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="127.0.0.1:6379&amp;gt;"&gt;quit
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, we'll look at renaming Redis commands which, if entered by mistake or by a malicious actor, could cause serious damage to your machine.&lt;/p&gt;

&lt;h2 id="step-5-—-renaming-dangerous-commands"&gt;Step 5 — Renaming Dangerous Commands&lt;/h2&gt;

&lt;p&gt;The other security feature built into Redis involves renaming or completely disabling certain commands that are considered dangerous.&lt;/p&gt;

&lt;p&gt;When run by unauthorized users, such commands can be used to reconfigure, destroy, or otherwise wipe your data. Like the authentication password, renaming or disabling commands is configured in the same &lt;code&gt;SECURITY&lt;/code&gt; section of the &lt;code&gt;/etc/redis/redis.conf&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Some of the commands that are considered dangerous include: &lt;strong&gt;FLUSHDB&lt;/strong&gt;, &lt;strong&gt;FLUSHALL&lt;/strong&gt;, &lt;strong&gt;KEYS&lt;/strong&gt;, &lt;strong&gt;PEXPIRE&lt;/strong&gt;, &lt;strong&gt;DEL&lt;/strong&gt;, &lt;strong&gt;CONFIG&lt;/strong&gt;, &lt;strong&gt;SHUTDOWN&lt;/strong&gt;, &lt;strong&gt;BGREWRITEAOF&lt;/strong&gt;, &lt;strong&gt;BGSAVE&lt;/strong&gt;, &lt;strong&gt;SAVE&lt;/strong&gt;, &lt;strong&gt;SPOP&lt;/strong&gt;, &lt;strong&gt;SREM&lt;/strong&gt;, &lt;strong&gt;RENAME&lt;/strong&gt;, and &lt;strong&gt;DEBUG&lt;/strong&gt;. This is not a comprehensive list, but renaming or disabling all of the commands in that list is a good starting point for enhancing your Redis server’s security.&lt;/p&gt;

&lt;p&gt;Whether you should disable or rename a command depends on your specific needs or those of your site. If you know you will never use a command that could be abused, then you may disable it. Otherwise, it might be in your best interest to rename it.&lt;/p&gt;

&lt;p&gt;To enable or disable Redis commands, open the configuration file once more:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano  /etc/redis/redis.conf
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span class='warning'&gt;&lt;strong&gt;Warning:&lt;/strong&gt; The following steps showing how to disable and rename commands are examples. You should only choose to disable or rename the commands that make sense for you. You can review the full list of commands for yourself and determine how they might be misused at &lt;a href="http://redis.io/commands"&gt;redis.io/commands&lt;/a&gt;.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;To disable a command, simply rename it to an empty string (signified by a pair of quotation marks with no characters between them), as shown below:&lt;/p&gt;
&lt;div class="code-label " title="/etc/redis/redis.conf"&gt;/etc/redis/redis.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .
# It is also possible to completely kill a command by renaming it into
# an empty string:
#
&lt;span class="highlight"&gt;rename-command FLUSHDB ""&lt;/span&gt;
&lt;span class="highlight"&gt;rename-command FLUSHALL ""&lt;/span&gt;
&lt;span class="highlight"&gt;rename-command DEBUG ""&lt;/span&gt;
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To rename a command, give it another name as shown in the examples below. Renamed commands should be difficult for others to guess, but easy for you to remember:&lt;/p&gt;
&lt;div class="code-label " title="/etc/redis/redis.conf"&gt;/etc/redis/redis.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .
# rename-command CONFIG ""
&lt;span class="highlight"&gt;rename-command SHUTDOWN SHUTDOWN_MENOT&lt;/span&gt;
&lt;span class="highlight"&gt;rename-command CONFIG ASC12_CONFIG&lt;/span&gt;
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save your changes and close the file.&lt;/p&gt;

&lt;p&gt;After renaming a command, apply the change by restarting Redis:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl restart redis
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To test the new command, enter the Redis command line:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;redis-cli
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, authenticate:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="127.0.0.1:6379&amp;gt;"&gt;auth &lt;span class="highlight"&gt;your_redis_password&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;OK
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let’s assume that you renamed the &lt;code&gt;CONFIG&lt;/code&gt; command to &lt;code&gt;ASC12_CONFIG&lt;/code&gt;, as in the preceding example. First, try using the original &lt;code&gt;CONFIG&lt;/code&gt; command. It should fail, because you’ve renamed it:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="127.0.0.1:6379&amp;gt;"&gt;config get requirepass
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;(error) ERR unknown command 'config'
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Calling the renamed command, however, will be successful. It is not case-sensitive:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="127.0.0.1:6379&amp;gt;"&gt;asc12_config get requirepass
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;1) "requirepass"
2) "&lt;span class="highlight"&gt;your_redis_password&lt;/span&gt;"
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, you can exit from &lt;code&gt;redis-cli&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="127.0.0.1:6379&amp;gt;"&gt;exit
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that if you're already using the Redis command line and then restart Redis, you'll need to re-authenticate. Otherwise, you'll get this error if you type a command:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;NOAUTH Authentication required.
&lt;/code&gt;&lt;/pre&gt;
&lt;span class='warning'&gt;&lt;p&gt;
Regarding the practice of renaming commands, there's a cautionary statement at the end of the &lt;code&gt;SECURITY&lt;/code&gt; section in &lt;code&gt;/etc/redis/redis.conf&lt;/code&gt; which reads:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;Please note that changing the name of commands that are logged into the AOF file or transmitted to slaves may cause problems.&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Note:&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;The Redis project chooses to use the terms “master” and “slave” while DigitalOcean generally prefers alternative descriptors. In order to avoid confusion we’ve chosen to use the terms used in the Redis documentation here.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;That means if the renamed command is not in the AOF file, or if it is but the AOF file has not been transmitted to slaves, then there should be no problem.&lt;/p&gt;

&lt;p&gt;So, keep that in mind when you're trying to rename commands. The best time to rename a command is when you're not using AOF persistence, or right after installation, that is, before your Redis-using application has been deployed.&lt;/p&gt;

&lt;p&gt;When you're using AOF and dealing with a master-slave installation, consider &lt;a href="https://github.com/antirez/redis/issues/2783"&gt;this answer from the project's GitHub issue page&lt;/a&gt;. The following is a reply to the author's question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The commands are logged to the AOF and replicated to the slave the same way they are sent, so if you try to replay the AOF on an instance that doesn't have the same renaming, you may face inconsistencies as the command cannot be executed (same for slaves).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Thus, the best way to handle renaming in cases like that is to make sure that renamed commands are applied to all instances in master-slave installations.&lt;br&gt;&lt;/p&gt;&lt;/span&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;In this tutorial, you installed and configured Redis, validated that your Redis installation is functioning correctly, and used its built-in security features to make it less vulnerable to attacks from malicious actors.&lt;/p&gt;

&lt;p&gt;Keep in mind that once someone is logged in to your server, it's very easy to circumvent the Redis-specific security features we've put in place. Therefore, the most important security feature on your Redis server is your firewall (which you configured if you followed the prerequisite &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-debian-9#step-four-%E2%80%94-setting-up-a-basic-firewall"&gt;Initial Server Setup&lt;/a&gt; tutorial), as this makes it extremely difficult for malicious actors to jump that fence.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-create-raid-arrays-with-mdadm-on-debian-9</id>
    <published>2018-09-05T20:20:35Z</published>
    <updated>2018-09-05T20:22:58Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-create-raid-arrays-with-mdadm-on-debian-9"/>
    <title>How To Create RAID Arrays with mdadm on Debian 9</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;mdadm&lt;/code&gt; utility can be used to create and manage storage arrays using Linux's software RAID capabilities.  Administrators have great flexibility in coordinating their individual storage devices and creating logical storage devices that have greater performance or redundancy characteristics.&lt;/p&gt;

&lt;p&gt;In this guide, we will go over a number of different RAID configurations that can be set up using a Debian 9 server.&lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;In order to complete the steps in this guide, you should have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;A non-root user with &lt;code&gt;sudo&lt;/code&gt; privileges on a Debian 9 server&lt;/strong&gt;: The steps in this guide will be completed with a &lt;code&gt;sudo&lt;/code&gt; user.  To learn how to set up an account with these privileges, follow our &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-debian-9"&gt;Debian 9 initial server setup guide&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A basic understanding of RAID terminology and concepts&lt;/strong&gt;: While this guide will touch on some RAID terminology in passing, a more complete understanding is very useful.  To learn more about RAID and to get a better understanding of what RAID level is right for you, read our &lt;a href="https://www.digitalocean.com/community/tutorials/an-introduction-to-raid-terminology-and-concepts"&gt;introduction to RAID article&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Multiple raw storage devices available on your server&lt;/strong&gt;: We will be demonstrating how to configure various types of arrays on the server.  As such, you will need some drives to configure.  If you are using DigitalOcean, you can use &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-use-block-storage-on-digitalocean"&gt;Block Storage volumes&lt;/a&gt; to fill this role.  Depending on the array type, you will need at minimum between &lt;strong&gt;two to four storage devices&lt;/strong&gt;.  These drives do not need to be formatted prior to following this guide.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="installing-the-raid-administration-tools"&gt;Installing the RAID Administration Tools&lt;/h2&gt;

&lt;p&gt;Before we begin, we need to install &lt;code&gt;mdadm&lt;/code&gt;, the tool that allows us to set up and manage software RAID arrays in Linux.  This is available in Debian's default repositories.&lt;/p&gt;

&lt;p&gt;Update the local package cache to retrieve an up-to-date list of available packages and then download and install the package:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt update
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo apt install mdadm
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will install &lt;code&gt;mdadm&lt;/code&gt; and all of its dependencies.  Verify that the utility is installed by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo mdadm -V
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;mdadm - v3.4 - 28th January 2016
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The application version should be displayed, indicating that &lt;code&gt;mdadm&lt;/code&gt; is installed and ready to use.&lt;/p&gt;

&lt;h2 id="resetting-existing-raid-devices"&gt;Resetting Existing RAID Devices&lt;/h2&gt;

&lt;p&gt;Throughout this guide, we will be introducing the steps to create a number of different RAID levels.  If you wish to follow along, you will likely want to reuse your storage devices after each section.  This section can be referenced to learn how to quickly reset your component storage devices prior to testing a new RAID level.  Skip this section for now if you have not yet set up any arrays.&lt;/p&gt;

&lt;p&gt;&lt;span class='warning'&gt;&lt;strong&gt;Warning:&lt;/strong&gt; This process will completely destroy the array and any data written to it.  Make sure that you are operating on the correct array and that you have copied off any data you need to retain prior to destroying the array.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;Find the active arrays in the &lt;code&gt;/proc/mdstat&lt;/code&gt; file by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cat /proc/mdstat
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Personalities : [raid0] [linear] [multipath] [raid1] [raid6] [raid5] [raid4] [raid10] 
&lt;span class="highlight"&gt;md0&lt;/span&gt; : active raid0 sdc[1] sdd[0]
      209584128 blocks super 1.2 512k chunks

            unused devices: &amp;lt;none&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Unmount the array from the filesystem:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo umount /dev/&lt;span class="highlight"&gt;md0&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, stop and remove the array by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo mdadm --stop /dev/&lt;span class="highlight"&gt;md0&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Find the devices that were used to build the array with the following command:&lt;/p&gt;

&lt;p&gt;&lt;span class='warning'&gt;&lt;strong&gt;Warning:&lt;/strong&gt; Keep in mind that the &lt;code&gt;/dev/sd*&lt;/code&gt; names can change any time you reboot!  Check them every time to make sure you are operating on the correct devices.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;lsblk -o NAME,SIZE,FSTYPE,TYPE,MOUNTPOINT
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;NAME     SIZE FSTYPE            TYPE MOUNTPOINT
sda      100G                   disk 
sdb      100G                   disk 
&lt;span class="highlight"&gt;sdc      100G linux_raid_member disk &lt;/span&gt;
&lt;span class="highlight"&gt;sdd      100G linux_raid_member disk &lt;/span&gt;
vda       25G                   disk 
├─vda1  24.9G ext4              part /
├─vda14    4M                   part 
└─vda15  106M vfat              part /boot/efi
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After discovering the devices used to create an array, zero their superblock to remove the RAID metadata and reset them to normal:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo mdadm --zero-superblock /dev/&lt;span class="highlight"&gt;sdc&lt;/span&gt;
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo mdadm --zero-superblock /dev/&lt;span class="highlight"&gt;sdd&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should remove any of the persistent references to the array.  Edit the &lt;code&gt;/etc/fstab&lt;/code&gt; file and comment out or remove the reference to your array:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/fstab
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class="code-label " title="/etc/fstab"&gt;/etc/fstab&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .
&lt;span class="highlight"&gt;#&lt;/span&gt; /dev/md0 /mnt/md0 ext4 defaults,nofail,discard 0 0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Also, comment out or remove the array definition from the &lt;code&gt;/etc/mdadm/mdadm.conf&lt;/code&gt; file:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/mdadm/mdadm.conf
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class="code-label " title="/etc/mdadm/mdadm.conf"&gt;/etc/mdadm/mdadm.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .
&lt;span class="highlight"&gt;#&lt;/span&gt; ARRAY /dev/md0 metadata=1.2 name=mdadmwrite:0 UUID=7261fb9c:976d0d97:30bc63ce:85e76e91
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, update the &lt;code&gt;initramfs&lt;/code&gt; again so that the early boot process does not try to bring an unavailable array online.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo update-initramfs -u
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At this point, you should be ready to reuse the storage devices individually, or as components of a different array.&lt;/p&gt;

&lt;h2 id="creating-a-raid-0-array"&gt;Creating a RAID 0 Array&lt;/h2&gt;

&lt;p&gt;The RAID 0 array works by breaking up data into chunks and striping it across the available disks.  This means that each disk contains a portion of the data and that multiple disks will be referenced when retrieving information.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Requirements: minimum of &lt;strong&gt;2 storage devices&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Primary benefit: Performance&lt;/li&gt;
&lt;li&gt;Things to keep in mind: Make sure that you have functional backups.  A single device failure will destroy all data in the array.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id="identifying-the-component-devices"&gt;Identifying the Component Devices&lt;/h3&gt;

&lt;p&gt;To get started, find the identifiers for the raw disks that you will be using:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;lsblk -o NAME,SIZE,FSTYPE,TYPE,MOUNTPOINT
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;NAME     SIZE FSTYPE TYPE MOUNTPOINT
&lt;span class="highlight"&gt;sda      100G        disk&lt;/span&gt;
&lt;span class="highlight"&gt;sdb      100G        disk&lt;/span&gt;
vda       25G        disk 
├─vda1  24.9G ext4   part /
├─vda14    4M        part 
└─vda15  106M vfat   part /boot/efi
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see above, we have two disks without a filesystem, each 100G in size.  In this example, these devices have been given the &lt;code&gt;/dev/sda&lt;/code&gt; and &lt;code&gt;/dev/sdb&lt;/code&gt; identifiers for this session.  These will be the raw components we will use to build the array.&lt;/p&gt;

&lt;h3 id="creating-the-array"&gt;Creating the Array&lt;/h3&gt;

&lt;p&gt;To create a RAID 0 array with these components, pass them in to the &lt;code&gt;mdadm --create&lt;/code&gt; command.  You will have to specify the device name you wish to create (&lt;code&gt;/dev/md0&lt;/code&gt; in our case), the RAID level, and the number of devices:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo mdadm --create --verbose /dev/md0 --level=0 --raid-devices=2 /dev/&lt;span class="highlight"&gt;sda&lt;/span&gt; /dev/&lt;span class="highlight"&gt;sdb&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can ensure that the RAID was successfully created by checking the &lt;code&gt;/proc/mdstat&lt;/code&gt; file:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cat /proc/mdstat
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Personalities : [raid0] 
&lt;span class="highlight"&gt;md0 : active raid0 sdb[1] sda[0]&lt;/span&gt;
      209584128 blocks super 1.2 512k chunks

unused devices: &amp;lt;none&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see in the highlighted line, the &lt;code&gt;/dev/md0&lt;/code&gt; device has been created in the RAID 0 configuration using the &lt;code&gt;/dev/sda&lt;/code&gt; and &lt;code&gt;/dev/sdb&lt;/code&gt; devices.&lt;/p&gt;

&lt;h3 id="creating-and-mounting-the-filesystem"&gt;Creating and Mounting the Filesystem&lt;/h3&gt;

&lt;p&gt;Next, create a filesystem on the array:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo mkfs.ext4 -F /dev/md0
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Create a mount point to attach the new filesystem:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo mkdir -p /mnt/md0
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can mount the filesystem by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo mount /dev/md0 /mnt/md0
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Check whether the new space is available by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;df -h -x devtmpfs -x tmpfs
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Filesystem      Size  Used Avail Use% Mounted on
/dev/vda1        25G 1003M   23G   5% /
&lt;span class="highlight"&gt;/dev/md0        196G   61M  186G   1% /mnt/md0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The new filesystem is mounted and accessible.&lt;/p&gt;

&lt;h3 id="saving-the-array-layout"&gt;Saving the Array Layout&lt;/h3&gt;

&lt;p&gt;To make sure that the array is reassembled automatically at boot, we will have to adjust the &lt;code&gt;/etc/mdadm/mdadm.conf&lt;/code&gt; file.  You can automatically scan the active array and append the file by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo mdadm --detail --scan | sudo tee -a /etc/mdadm/mdadm.conf
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Afterwards, you can update the initramfs, or initial RAM file system, so that the array will be available during the early boot process:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo update-initramfs -u
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add the new filesystem mount options to the &lt;code&gt;/etc/fstab&lt;/code&gt; file for automatic mounting at boot:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;echo '/dev/md0 /mnt/md0 ext4 defaults,nofail,discard 0 0' | sudo tee -a /etc/fstab
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Your RAID 0 array should now automatically be assembled and mounted each boot.&lt;/p&gt;

&lt;h2 id="creating-a-raid-1-array"&gt;Creating a RAID 1 Array&lt;/h2&gt;

&lt;p&gt;The RAID 1 array type is implemented by mirroring data across all available disks.  Each disk in a RAID 1 array gets a full copy of the data, providing redundancy in the event of a device failure.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Requirements: minimum of &lt;strong&gt;2 storage devices&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Primary benefit: Redundancy&lt;/li&gt;
&lt;li&gt;Things to keep in mind: Since two copies of the data are maintained, only half of the disk space will be usable&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id="identifying-the-component-devices"&gt;Identifying the Component Devices&lt;/h3&gt;

&lt;p&gt;To get started, find the identifiers for the raw disks that you will be using:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;lsblk -o NAME,SIZE,FSTYPE,TYPE,MOUNTPOINT
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;NAME     SIZE FSTYPE TYPE MOUNTPOINT
&lt;span class="highlight"&gt;sda      100G        disk&lt;/span&gt;
&lt;span class="highlight"&gt;sdb      100G        disk&lt;/span&gt;
vda       25G        disk 
├─vda1  24.9G ext4   part /
├─vda14    4M        part 
└─vda15  106M vfat   part /boot/efi
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see above, we have two disks without a filesystem, each 100G in size.  In this example, these devices have been given the &lt;code&gt;/dev/sda&lt;/code&gt; and &lt;code&gt;/dev/sdb&lt;/code&gt; identifiers for this session.  These will be the raw components we will use to build the array.&lt;/p&gt;

&lt;h3 id="creating-the-array"&gt;Creating the Array&lt;/h3&gt;

&lt;p&gt;To create a RAID 1 array with these components, pass them in to the &lt;code&gt;mdadm --create&lt;/code&gt; command.  You will have to specify the device name you wish to create (&lt;code&gt;/dev/md0&lt;/code&gt; in our case), the RAID level, and the number of devices:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo mdadm --create --verbose /dev/md0 --level=1 --raid-devices=2 /dev/&lt;span class="highlight"&gt;sda&lt;/span&gt; /dev/&lt;span class="highlight"&gt;sdb&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If the component devices you are using are not partitions with the &lt;code&gt;boot&lt;/code&gt; flag enabled, you will likely see the following warning.  It is safe to type &lt;strong&gt;y&lt;/strong&gt; to continue:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;mdadm: Note: this array has metadata at the start and
    may not be suitable as a boot device.  If you plan to
    store '/boot' on this device please ensure that
    your boot-loader understands md/v1.x metadata, or use
    --metadata=0.90
mdadm: size set to 104792064K
Continue creating array? &lt;span class="highlight"&gt;y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;mdadm&lt;/code&gt; tool will start to mirror the drives.  This can take some time to complete, but the array can be used during this time.  You can monitor the progress of the mirroring by checking the &lt;code&gt;/proc/mdstat&lt;/code&gt; file:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cat /proc/mdstat
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Personalities : [raid0] [linear] [multipath] [raid1] [raid6] [raid5] [raid4] [raid10] 
&lt;span class="highlight"&gt;md0 : active raid1 sdb[1] sda[0]&lt;/span&gt;
      104792064 blocks super 1.2 [2/2] [UU]
      &lt;span class="highlight"&gt;[&amp;gt;....................]  resync =  1.5% (1629632/104792064) finish=8.4min speed=203704K/sec&lt;/span&gt;

unused devices: &amp;lt;none&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see in the first highlighted line, the &lt;code&gt;/dev/md0&lt;/code&gt; device has been created in the RAID 1 configuration using the &lt;code&gt;/dev/sda&lt;/code&gt; and &lt;code&gt;/dev/sdb&lt;/code&gt; devices.  The second highlighted line shows the progress on the mirroring.  You can continue the guide while this process completes.&lt;/p&gt;

&lt;h3 id="creating-and-mounting-the-filesystem"&gt;Creating and Mounting the Filesystem&lt;/h3&gt;

&lt;p&gt;Next, create a filesystem on the array:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo mkfs.ext4 -F /dev/md0
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Create a mount point to attach the new filesystem:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo mkdir -p /mnt/md0
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can mount the filesystem by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo mount /dev/md0 /mnt/md0
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Check whether the new space is available by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;df -h -x devtmpfs -x tmpfs
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Filesystem      Size  Used Avail Use% Mounted on
/dev/vda1        25G 1003M   23G   5% /
&lt;span class="highlight"&gt;/dev/md0         98G   61M   93G   1% /mnt/md0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The new filesystem is mounted and accessible.&lt;/p&gt;

&lt;h3 id="saving-the-array-layout"&gt;Saving the Array Layout&lt;/h3&gt;

&lt;p&gt;To make sure that the array is reassembled automatically at boot, we will have to adjust the &lt;code&gt;/etc/mdadm/mdadm.conf&lt;/code&gt; file.  You can automatically scan the active array and append the file by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo mdadm --detail --scan | sudo tee -a /etc/mdadm/mdadm.conf
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Afterwards, you can update the initramfs, or initial RAM file system, so that the array will be available during the early boot process:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo update-initramfs -u
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add the new filesystem mount options to the &lt;code&gt;/etc/fstab&lt;/code&gt; file for automatic mounting at boot:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;echo '/dev/md0 /mnt/md0 ext4 defaults,nofail,discard 0 0' | sudo tee -a /etc/fstab
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Your RAID 1 array should now automatically be assembled and mounted each boot.&lt;/p&gt;

&lt;h2 id="creating-a-raid-5-array"&gt;Creating a RAID 5 Array&lt;/h2&gt;

&lt;p&gt;The RAID 5 array type is implemented by striping data across the available devices.  One component of each stripe is a calculated parity block.  If a device fails, the parity block and the remaining blocks can be used to calculate the missing data.  The device that receives the parity block is rotated so that each device has a balanced amount of parity information.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Requirements: minimum of &lt;strong&gt;3 storage devices&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Primary benefit: Redundancy with more usable capacity.&lt;/li&gt;
&lt;li&gt;Things to keep in mind: While the parity information is distributed, one disk's worth of capacity will be used for parity.  RAID 5 can suffer from very poor performance when in a degraded state.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id="identifying-the-component-devices"&gt;Identifying the Component Devices&lt;/h3&gt;

&lt;p&gt;To get started, find the identifiers for the raw disks that you will be using:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;lsblk -o NAME,SIZE,FSTYPE,TYPE,MOUNTPOINT
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;NAME     SIZE FSTYPE TYPE MOUNTPOINT
&lt;span class="highlight"&gt;sda      100G        disk&lt;/span&gt;
&lt;span class="highlight"&gt;sdb      100G        disk&lt;/span&gt;
&lt;span class="highlight"&gt;sdc      100G        disk&lt;/span&gt;
vda       25G        disk 
├─vda1  24.9G ext4   part /
├─vda14    4M        part 
└─vda15  106M vfat   part /boot/efi
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see above, we have three disks without a filesystem, each 100G in size.  In this example, these devices have been given the &lt;code&gt;/dev/sda&lt;/code&gt;, &lt;code&gt;/dev/sdb&lt;/code&gt;, and &lt;code&gt;/dev/sdc&lt;/code&gt; identifiers for this session.  These will be the raw components we will use to build the array.&lt;/p&gt;

&lt;h3 id="creating-the-array"&gt;Creating the Array&lt;/h3&gt;

&lt;p&gt;To create a RAID 5 array with these components, pass them in to the &lt;code&gt;mdadm --create&lt;/code&gt; command.  You will have to specify the device name you wish to create (&lt;code&gt;/dev/md0&lt;/code&gt; in our case), the RAID level, and the number of devices:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo mdadm --create --verbose /dev/md0 --level=5 --raid-devices=3 /dev/&lt;span class="highlight"&gt;sda&lt;/span&gt; /dev/&lt;span class="highlight"&gt;sdb&lt;/span&gt; /dev/&lt;span class="highlight"&gt;sdc&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;mdadm&lt;/code&gt; tool will start to configure the array (it actually uses the recovery process to build the array for performance reasons).  This can take some time to complete, but the array can be used during this time.  You can monitor the progress of the mirroring by checking the &lt;code&gt;/proc/mdstat&lt;/code&gt; file:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cat /proc/mdstat
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Personalities : [raid0] [linear] [multipath] [raid1] [raid6] [raid5] [raid4] [raid10] 
&lt;span class="highlight"&gt;md0 : active raid5 sdc[3] sdb[1] sda[0]&lt;/span&gt;
      209584128 blocks super 1.2 level 5, 512k chunk, algorithm 2 [3/2] [UU_]
      &lt;span class="highlight"&gt;[&amp;gt;....................]  recovery =  0.9% (1031612/104792064) finish=10.0min speed=171935K/sec&lt;/span&gt;

unused devices: &amp;lt;none&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see in the first highlighted line, the &lt;code&gt;/dev/md0&lt;/code&gt; device has been created in the RAID 5 configuration using the &lt;code&gt;/dev/sda&lt;/code&gt;, &lt;code&gt;/dev/sdb&lt;/code&gt; and &lt;code&gt;/dev/sdc&lt;/code&gt; devices.  The second highlighted line shows the progress on the build.&lt;/p&gt;

&lt;p&gt;&lt;span class='warning'&gt;&lt;strong&gt;Warning:&lt;/strong&gt; Due to the way that &lt;code&gt;mdadm&lt;/code&gt; builds RAID 5 arrays, while the array is still building, the number of spares in the array will be inaccurately reported.  This means that you must wait for the array to finish assembling before updating the &lt;code&gt;/etc/mdadm/mdadm.conf&lt;/code&gt; file.  If you update the configuration file while the array is still building, the system will have incorrect information about the array state and will be unable to assemble it automatically at boot with the correct name.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;You can continue the guide while this process completes.&lt;/p&gt;

&lt;h3 id="creating-and-mounting-the-filesystem"&gt;Creating and Mounting the Filesystem&lt;/h3&gt;

&lt;p&gt;Next, create a filesystem on the array:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo mkfs.ext4 -F /dev/md0
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Create a mount point to attach the new filesystem:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo mkdir -p /mnt/md0
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can mount the filesystem by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo mount /dev/md0 /mnt/md0
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Check whether the new space is available by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;df -h -x devtmpfs -x tmpfs
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Filesystem      Size  Used Avail Use% Mounted on
/dev/vda1        25G 1003M   23G   5% /
&lt;span class="highlight"&gt;/dev/md0        196G   61M  186G   1% /mnt/md0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The new filesystem is mounted and accessible.&lt;/p&gt;

&lt;h3 id="saving-the-array-layout"&gt;Saving the Array Layout&lt;/h3&gt;

&lt;p&gt;To make sure that the array is reassembled automatically at boot, we will have to adjust the &lt;code&gt;/etc/mdadm/mdadm.conf&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;As mentioned above, before you adjust the configuration, check again to make sure the array has finished assembling.&lt;/strong&gt;  Completing this step before the array is built will prevent the system from assembling the array correctly on reboot:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cat /proc/mdstat
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Personalities : [raid1] [linear] [multipath] [raid0] [raid6] [raid5] [raid4] [raid10] 
md0 : active raid5 sdc[3] sdb[1] sda[0]
      209584128 blocks super 1.2 level 5, 512k chunk, algorithm 2 [3/3] [UUU]

unused devices: &amp;lt;none&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The output above shows that the rebuild is complete.  Now, we can automatically scan the active array and append the file by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo mdadm --detail --scan | sudo tee -a /etc/mdadm/mdadm.conf
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Afterwards, you can update the initramfs, or initial RAM file system, so that the array will be available during the early boot process:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo update-initramfs -u
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add the new filesystem mount options to the &lt;code&gt;/etc/fstab&lt;/code&gt; file for automatic mounting at boot:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;echo '/dev/md0 /mnt/md0 ext4 defaults,nofail,discard 0 0' | sudo tee -a /etc/fstab
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Your RAID 5 array should now automatically be assembled and mounted each boot.&lt;/p&gt;

&lt;h2 id="creating-a-raid-6-array"&gt;Creating a RAID 6 Array&lt;/h2&gt;

&lt;p&gt;The RAID 6 array type is implemented by striping data across the available devices.  Two components of each stripe are calculated parity blocks.  If one or two devices fail, the parity blocks and the remaining blocks can be used to calculate the missing data.  The devices that receive the parity blocks are rotated so that each device has a balanced amount of parity information.  This is similar to a RAID 5 array, but allows for the failure of two drives.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Requirements: minimum of &lt;strong&gt;4 storage devices&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Primary benefit: Double redundancy with more usable capacity.&lt;/li&gt;
&lt;li&gt;Things to keep in mind: While the parity information is distributed, two disk's worth of capacity will be used for parity.  RAID 6 can suffer from very poor performance when in a degraded state.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id="identifying-the-component-devices"&gt;Identifying the Component Devices&lt;/h3&gt;

&lt;p&gt;To get started, find the identifiers for the raw disks that you will be using:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;lsblk -o NAME,SIZE,FSTYPE,TYPE,MOUNTPOINT
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;NAME     SIZE FSTYPE TYPE MOUNTPOINT
&lt;span class="highlight"&gt;sda      100G        disk&lt;/span&gt;
&lt;span class="highlight"&gt;sdb      100G        disk&lt;/span&gt;
&lt;span class="highlight"&gt;sdc      100G        disk&lt;/span&gt;
&lt;span class="highlight"&gt;sdd      100G        disk&lt;/span&gt;
vda       25G        disk 
├─vda1  24.9G ext4   part /
├─vda14    4M        part 
└─vda15  106M vfat   part /boot/efi
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see above, we have four disks without a filesystem, each 100G in size.  In this example, these devices have been given the &lt;code&gt;/dev/sda&lt;/code&gt;, &lt;code&gt;/dev/sdb&lt;/code&gt;, &lt;code&gt;/dev/sdc&lt;/code&gt;, and &lt;code&gt;/dev/sdd&lt;/code&gt; identifiers for this session.  These will be the raw components we will use to build the array.&lt;/p&gt;

&lt;h3 id="creating-the-array"&gt;Creating the Array&lt;/h3&gt;

&lt;p&gt;To create a RAID 6 array with these components, pass them in to the &lt;code&gt;mdadm --create&lt;/code&gt; command.  You will have to specify the device name you wish to create (&lt;code&gt;/dev/md0&lt;/code&gt; in our case), the RAID level, and the number of devices:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo mdadm --create --verbose /dev/md0 --level=6 --raid-devices=4 /dev/&lt;span class="highlight"&gt;sda&lt;/span&gt; /dev/&lt;span class="highlight"&gt;sdb&lt;/span&gt; /dev/&lt;span class="highlight"&gt;sdc&lt;/span&gt; /dev/&lt;span class="highlight"&gt;sdd&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;mdadm&lt;/code&gt; tool will start to configure the array (it actually uses the recovery process to build the array for performance reasons).  This can take some time to complete, but the array can be used during this time.  You can monitor the progress of the mirroring by checking the &lt;code&gt;/proc/mdstat&lt;/code&gt; file:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cat /proc/mdstat
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Personalities : [raid0] [linear] [multipath] [raid1] [raid6] [raid5] [raid4] [raid10] 
&lt;span class="highlight"&gt;md0 : active raid6 sdd[3] sdc[2] sdb[1] sda[0]&lt;/span&gt;
      209584128 blocks super 1.2 level 6, 512k chunk, algorithm 2 [4/4] [UUUU]
      &lt;span class="highlight"&gt;[&amp;gt;....................]  resync =  0.3% (353056/104792064) finish=14.7min speed=117685K/sec&lt;/span&gt;

unused devices: &amp;lt;none&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see in the first highlighted line, the &lt;code&gt;/dev/md0&lt;/code&gt; device has been created in the RAID 6 configuration using the &lt;code&gt;/dev/sda&lt;/code&gt;, &lt;code&gt;/dev/sdb&lt;/code&gt;, &lt;code&gt;/dev/sdc&lt;/code&gt; and &lt;code&gt;/dev/sdd&lt;/code&gt; devices.  The second highlighted line shows the progress on the build.  You can continue the guide while this process completes.&lt;/p&gt;

&lt;h3 id="creating-and-mounting-the-filesystem"&gt;Creating and Mounting the Filesystem&lt;/h3&gt;

&lt;p&gt;Next, create a filesystem on the array:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo mkfs.ext4 -F /dev/md0
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Create a mount point to attach the new filesystem:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo mkdir -p /mnt/md0
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can mount the filesystem by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo mount /dev/md0 /mnt/md0
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Check whether the new space is available by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;df -h -x devtmpfs -x tmpfs
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Filesystem      Size  Used Avail Use% Mounted on
/dev/vda1        25G 1003M   23G   5% /
&lt;span class="highlight"&gt;/dev/md0        196G   61M  186G   1% /mnt/md0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The new filesystem is mounted and accessible.&lt;/p&gt;

&lt;h3 id="save-the-array-layout"&gt;Save the Array Layout&lt;/h3&gt;

&lt;p&gt;To make sure that the array is reassembled automatically at boot, we will have to adjust the &lt;code&gt;/etc/mdadm/mdadm.conf&lt;/code&gt; file.  We can automatically scan the active array and append the file by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo mdadm --detail --scan | sudo tee -a /etc/mdadm/mdadm.conf
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Afterwards, you can update the initramfs, or initial RAM file system, so that the array will be available during the early boot process:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo update-initramfs -u
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add the new filesystem mount options to the &lt;code&gt;/etc/fstab&lt;/code&gt; file for automatic mounting at boot:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;echo '/dev/md0 /mnt/md0 ext4 defaults,nofail,discard 0 0' | sudo tee -a /etc/fstab
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Your RAID 6 array should now automatically be assembled and mounted each boot.&lt;/p&gt;

&lt;h2 id="creating-a-complex-raid-10-array"&gt;Creating a Complex RAID 10 Array&lt;/h2&gt;

&lt;p&gt;The RAID 10 array type is traditionally implemented by creating a striped RAID 0 array composed of sets of RAID 1 arrays.  This nested array type gives both redundancy and high performance, at the expense of large amounts of disk space.  The &lt;code&gt;mdadm&lt;/code&gt; utility has its own RAID 10 type that provides the same type of benefits with increased flexibility.  It is &lt;em&gt;not&lt;/em&gt; created by nesting arrays, but has many of the same characteristics and guarantees.  We will be using the &lt;code&gt;mdadm&lt;/code&gt; RAID 10 here.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Requirements: minimum of &lt;strong&gt;3 storage devices&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Primary benefit: Performance and redundancy&lt;/li&gt;
&lt;li&gt;Things to keep in mind: The amount of capacity reduction for the array is defined by the number of data copies you choose to keep.  The number of copies that are stored with &lt;code&gt;mdadm&lt;/code&gt; style RAID 10 is configurable.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By default, two copies of each data block will be stored in what is called the "near" layout.  The possible layouts that dictate how each data block is stored are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;near&lt;/strong&gt;: The default arrangement.  Copies of each chunk are written consecutively when striping, meaning that the copies of the data blocks will be written around the same part of multiple disks.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;far&lt;/strong&gt;: The first and subsequent copies are written to different parts the storage devices in the array.  For instance, the first chunk might be written near the beginning of a disk, while the second chunk would be written half way down on a different disk.  This can give some read performance gains for traditional spinning disks at the expense of write performance.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;offset&lt;/strong&gt;: Each stripe is copied, offset by one drive.  This means that the copies are offset from one another, but still close together on the disk.  This helps minimize excessive seeking during some workloads.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can find out more about these layouts by checking out the "RAID10" section of this &lt;code&gt;man&lt;/code&gt; page:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;man 4 md
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can also find this &lt;code&gt;man&lt;/code&gt; page online &lt;a href="http://manpages.ubuntu.com/manpages/bionic/man4/md.4.html"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id="identifying-the-component-devices"&gt;Identifying the Component Devices&lt;/h3&gt;

&lt;p&gt;To get started, find the identifiers for the raw disks that you will be using:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;lsblk -o NAME,SIZE,FSTYPE,TYPE,MOUNTPOINT
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;NAME     SIZE FSTYPE TYPE MOUNTPOINT
&lt;span class="highlight"&gt;sda      100G        disk&lt;/span&gt;
&lt;span class="highlight"&gt;sdb      100G        disk&lt;/span&gt;
&lt;span class="highlight"&gt;sdc      100G        disk&lt;/span&gt;
&lt;span class="highlight"&gt;sdd      100G        disk&lt;/span&gt;
vda       25G        disk 
├─vda1  24.9G ext4   part /
├─vda14    4M        part 
└─vda15  106M vfat   part /boot/efi
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see above, we have four disks without a filesystem, each 100G in size.  In this example, these devices have been given the &lt;code&gt;/dev/sda&lt;/code&gt;, &lt;code&gt;/dev/sdb&lt;/code&gt;, &lt;code&gt;/dev/sdc&lt;/code&gt;, and &lt;code&gt;/dev/sdd&lt;/code&gt; identifiers for this session.  These will be the raw components we will use to build the array.&lt;/p&gt;

&lt;h3 id="creating-the-array"&gt;Creating the Array&lt;/h3&gt;

&lt;p&gt;To create a RAID 10 array with these components, pass them in to the &lt;code&gt;mdadm --create&lt;/code&gt; command.  You will have to specify the device name you wish to create (&lt;code&gt;/dev/md0&lt;/code&gt; in our case), the RAID level, and the number of devices.&lt;/p&gt;

&lt;p&gt;You can set up two copies using the near layout by not specifying a layout and copy number:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo mdadm --create --verbose /dev/md0 --level=10 --raid-devices=4 /dev/&lt;span class="highlight"&gt;sda&lt;/span&gt; /dev/&lt;span class="highlight"&gt;sdb&lt;/span&gt; /dev/&lt;span class="highlight"&gt;sdc&lt;/span&gt; /dev/&lt;span class="highlight"&gt;sdd&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you want to use a different layout, or change the number of copies, you will have to use the &lt;code&gt;--layout=&lt;/code&gt; option, which takes a layout and copy identifier.  The layouts are &lt;strong&gt;n&lt;/strong&gt; for near, &lt;strong&gt;f&lt;/strong&gt; for far, and &lt;strong&gt;o&lt;/strong&gt; for offset.  The number of copies to store is appended afterwards.&lt;/p&gt;

&lt;p&gt;For instance, to create an array that has 3 copies in the offset layout, the command would look like this:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo mdadm --create --verbose /dev/md0 --level=10 --layout=o3 --raid-devices=4 /dev/&lt;span class="highlight"&gt;sda&lt;/span&gt; /dev/&lt;span class="highlight"&gt;sdb&lt;/span&gt; /dev/&lt;span class="highlight"&gt;sdc&lt;/span&gt; /dev/&lt;span class="highlight"&gt;sdd&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;mdadm&lt;/code&gt; tool will start to configure the array (it actually uses the recovery process to build the array for performance reasons).  This can take some time to complete, but the array can be used during this time.  You can monitor the progress of the mirroring by checking the &lt;code&gt;/proc/mdstat&lt;/code&gt; file:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cat /proc/mdstat
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Personalities : [raid0] [linear] [multipath] [raid1] [raid6] [raid5] [raid4] [raid10] 
&lt;span class="highlight"&gt;md0 : active raid10 sdd[3] sdc[2] sdb[1] sda[0]&lt;/span&gt;
      209584128 blocks super 1.2 512K chunks 2 near-copies [4/4] [UUUU]
      &lt;span class="highlight"&gt;[&amp;gt;....................]  resync =  1.3% (2832768/209584128) finish=15.8min speed=217905K/sec&lt;/span&gt;

unused devices: &amp;lt;none&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see in the first highlighted line, the &lt;code&gt;/dev/md0&lt;/code&gt; device has been created in the RAID 10 configuration using the &lt;code&gt;/dev/sda&lt;/code&gt;, &lt;code&gt;/dev/sdb&lt;/code&gt;, &lt;code&gt;/dev/sdc&lt;/code&gt; and &lt;code&gt;/dev/sdd&lt;/code&gt; devices.  The second highlighted area shows the layout that was used for this example (2 copies in the near configuration).  The third highlighted area shows the progress on the build.  You can continue the guide while this process completes.&lt;/p&gt;

&lt;h3 id="creating-and-mounting-the-filesystem"&gt;Creating and Mounting the Filesystem&lt;/h3&gt;

&lt;p&gt;Next, create a filesystem on the array:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo mkfs.ext4 -F /dev/md0
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Create a mount point to attach the new filesystem:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo mkdir -p /mnt/md0
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can mount the filesystem by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo mount /dev/md0 /mnt/md0
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Check whether the new space is available by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;df -h -x devtmpfs -x tmpfs
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Filesystem      Size  Used Avail Use% Mounted on
/dev/vda1        25G 1003M   23G   5% /
/dev/md0        196G   61M  186G   1% /mnt/md0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The new filesystem is mounted and accessible.&lt;/p&gt;

&lt;h3 id="saving-the-array-layout"&gt;Saving the Array Layout&lt;/h3&gt;

&lt;p&gt;To make sure that the array is reassembled automatically at boot, we will have to adjust the &lt;code&gt;/etc/mdadm/mdadm.conf&lt;/code&gt; file.  We can automatically scan the active array and append the file by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo mdadm --detail --scan | sudo tee -a /etc/mdadm/mdadm.conf
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Afterwards, you can update the initramfs, or initial RAM file system, so that the array will be available during the early boot process:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo update-initramfs -u
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add the new filesystem mount options to the &lt;code&gt;/etc/fstab&lt;/code&gt; file for automatic mounting at boot:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;echo '/dev/md0 /mnt/md0 ext4 defaults,nofail,discard 0 0' | sudo tee -a /etc/fstab
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Your RAID 10 array should now automatically be assembled and mounted each boot.&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;In this guide, we demonstrated how to create various types of arrays using Linux's &lt;code&gt;mdadm&lt;/code&gt; software RAID utility.  RAID arrays offer some compelling redundancy and performance enhancements over using multiple disks individually.&lt;/p&gt;

&lt;p&gt;Once you have settled on the type of array needed for your environment and created the device, you will need to learn how to perform day-to-day management with &lt;code&gt;mdadm&lt;/code&gt;.  Our guide on &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-manage-raid-arrays-with-mdadm-on-ubuntu-16-04"&gt;how to manage RAID arrays with &lt;code&gt;mdadm&lt;/code&gt;&lt;/a&gt; can help get you started.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-install-the-apache-web-server-on-debian-9</id>
    <published>2018-09-05T19:48:01Z</published>
    <updated>2018-09-05T19:53:51Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-install-the-apache-web-server-on-debian-9"/>
    <title>How To Install the Apache Web Server on Debian 9</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;The Apache HTTP server is the most widely-used web server in the world.  It provides many powerful features including dynamically loadable modules, robust media support, and extensive integration with other popular software.&lt;/p&gt;

&lt;p&gt;In this guide, we'll explain how to install an Apache web server on your Debian 9 server.&lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;Before you begin this guide, you should have a regular, non-root user with sudo privileges configured on your server.  Additionally, you will need to enable a basic firewall to block non-essential ports.  You can learn how to configure a regular user account and set up a firewall for your server by following our &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-debian-9"&gt;initial server setup guide for Debian 9&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;When you have an account available, log in as your non-root user to begin.&lt;/p&gt;

&lt;h2 id="step-1-—-installing-apache"&gt;Step 1 — Installing Apache&lt;/h2&gt;

&lt;p&gt;Apache is available within Debian's default software repositories, making it possible to install it using conventional package management tools.&lt;/p&gt;

&lt;p&gt;Let's begin by updating the local package index to reflect the latest upstream changes:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt update
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, install the &lt;code&gt;apache2&lt;/code&gt; package:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install apache2
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After confirming the installation, &lt;code&gt;apt&lt;/code&gt; will install Apache and all required dependencies.&lt;/p&gt;

&lt;h2 id="step-2-—-adjusting-the-firewall"&gt;Step 2 — Adjusting the Firewall&lt;/h2&gt;

&lt;p&gt;Before testing Apache, it's necessary to modify the firewall settings to allow outside access to the default web ports.  Assuming that you followed the instructions in the prerequisites, you should have a UFW firewall configured to restrict access to your server.&lt;/p&gt;

&lt;p&gt;During installation, Apache registers itself with UFW to provide a few application profiles that can be used to enable or disable access to Apache through the firewall.&lt;/p&gt;

&lt;p&gt;List the &lt;code&gt;ufw&lt;/code&gt; application profiles by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw app list
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will see a list of the application profiles:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Available applications:
  AIM
  Bonjour
  CIFS
. . . 
 WWW
 WWW Cache
 WWW Full
 WWW Secure
. . . 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The Apache profiles begin with WWW:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;WWW&lt;/strong&gt;: This profile opens only port 80 (normal, unencrypted web traffic)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;WWW Cache&lt;/strong&gt;: This profile opens only port 8080 (sometimes used for caching and web proxies)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;WWW Full&lt;/strong&gt;: This profile opens both port 80 (normal, unencrypted web traffic) and port 443 (TLS/SSL encrypted traffic)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;WWW Secure&lt;/strong&gt;: This profile opens only port 443 (TLS/SSL encrypted traffic)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is recommended that you enable the most restrictive profile that will still allow the traffic you've configured.  Since we haven't configured SSL for our server yet in this guide, we will only need to allow traffic on port 80:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw allow 'WWW'
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can verify the change by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw status
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see HTTP traffic allowed in the displayed output:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Status: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere
WWW                        ALLOW       Anywhere
OpenSSH (v6)               ALLOW       Anywhere (v6)
WWW (v6)                   ALLOW       Anywhere (v6)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, the profile has been activated to allow access to the web server.&lt;/p&gt;

&lt;h2 id="step-3-—-checking-your-web-server"&gt;Step 3 — Checking your Web Server&lt;/h2&gt;

&lt;p&gt;At the end of the installation process, Debian 9 starts Apache.  The web server should already be up and running.&lt;/p&gt;

&lt;p&gt;Check with the &lt;code&gt;systemd&lt;/code&gt; init system to make sure the service is running by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl status apache2
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;● apache2.service - The Apache HTTP Server
   Loaded: loaded (/lib/systemd/system/apache2.service; enabled; vendor preset: enabled)
   Active: active (running) since Wed 2018-09-05 19:21:48 UTC; 13min ago
 Main PID: 12849 (apache2)
   CGroup: /system.slice/apache2.service
           ├─12849 /usr/sbin/apache2 -k start
           ├─12850 /usr/sbin/apache2 -k start
           └─12852 /usr/sbin/apache2 -k start

Sep 05 19:21:48 apache systemd[1]: Starting The Apache HTTP Server...
Sep 05 19:21:48 apache systemd[1]: Started The Apache HTTP Server.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see from this output, the service appears to have started successfully.  However, the best way to test this is to request a page from Apache.&lt;/p&gt;

&lt;p&gt;You can access the default Apache landing page to confirm that the software is running properly through your IP address. If you do not know your server's IP address, you can get it a few different ways from the command line.&lt;/p&gt;

&lt;p&gt;Try typing this at your server's command prompt:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;hostname -I
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will get back a few addresses separated by spaces.  You can try each in your web browser to see if they work.&lt;/p&gt;

&lt;p&gt;An alternative is using the &lt;code&gt;curl&lt;/code&gt; tool, which should give you your public IP address as seen from another location on the internet.&lt;/p&gt;

&lt;p&gt;First, install &lt;code&gt;curl&lt;/code&gt; using &lt;code&gt;apt&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install curl
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, use &lt;code&gt;curl&lt;/code&gt; to retrieve icanhazip.com using IPv4:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;curl -4 icanhazip.com
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When you have your server's IP address, enter it into your browser's address bar:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;http://&lt;span class="highlight"&gt;your_server_ip&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see the default Debian 9 Apache web page:&lt;/p&gt;

&lt;p&gt;&lt;img src="http://assets.digitalocean.com/how-to-install-lamp-debian-9/small_apache_default_debian9.png" alt="Apache default page"&gt;&lt;/p&gt;

&lt;p&gt;This page indicates that Apache is working correctly.  It also includes some basic information about important Apache files and directory locations.&lt;/p&gt;

&lt;h2 id="step-4-—-managing-the-apache-process"&gt;Step 4 — Managing the Apache Process&lt;/h2&gt;

&lt;p&gt;Now that you have your web server up and running, let's go over some basic management commands.&lt;/p&gt;

&lt;p&gt;To stop your web server, type:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl stop apache2
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To start the web server when it is stopped, type:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl start apache2
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To stop and then start the service again, type:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl restart apache2
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you are simply making configuration changes, Apache can often reload without dropping connections.  To do this, use this command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl reload apache2
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By default, Apache is configured to start automatically when the server boots.  If this is not what you want, disable this behavior by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl disable apache2
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To re-enable the service to start up at boot, type:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl enable apache2
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Apache should now start automatically when the server boots again.&lt;/p&gt;

&lt;h2 id="step-5-—-setting-up-virtual-hosts-recommended"&gt;Step 5 — Setting Up Virtual Hosts (Recommended)&lt;/h2&gt;

&lt;p&gt;When using the Apache web server, you can use &lt;em&gt;virtual hosts&lt;/em&gt; (similar to server blocks in Nginx) to encapsulate configuration details and host more than one domain from a single server. We will set up a domain called &lt;strong&gt;example.com&lt;/strong&gt;, but you should &lt;strong&gt;replace this with your own domain name&lt;/strong&gt;. To learn more about setting up a domain name with DigitalOcean, see our &lt;a href="https://www.digitalocean.com/community/tutorials/an-introduction-to-digitalocean-dns"&gt;Introduction to DigitalOcean DNS&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Apache on Debian 9 has one server block enabled by default that is configured to serve documents from the &lt;code&gt;/var/www/html&lt;/code&gt; directory. While this works well for a single site, it can become unwieldy if you are hosting multiple sites. Instead of modifying &lt;code&gt;/var/www/html&lt;/code&gt;, let's create a directory structure within &lt;code&gt;/var/www&lt;/code&gt; for our &lt;strong&gt;example.com&lt;/strong&gt; site, leaving &lt;code&gt;/var/www/html&lt;/code&gt; in place as the default directory to be served if a client request doesn't match any other sites.&lt;/p&gt;

&lt;p&gt;Create the directory for &lt;strong&gt;example.com&lt;/strong&gt; as follows, using the &lt;code&gt;-p&lt;/code&gt; flag to create any necessary parent directories:&lt;/p&gt;
&lt;pre class="code-pre commmand"&gt;&lt;code langs=""&gt;sudo mkdir -p /var/www/&lt;span class="highlight"&gt;example.com&lt;/span&gt;/html
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, assign ownership of the directory with the &lt;code&gt;$USER&lt;/code&gt; environmental variable:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo chown -R $USER:$USER /var/www/&lt;span class="highlight"&gt;example.com&lt;/span&gt;/html
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The permissions of your web roots should be correct if you haven't modified your &lt;code&gt;unmask&lt;/code&gt; value, but you can make sure by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo chmod -R 755 /var/www/&lt;span class="highlight"&gt;example.com&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, create a sample &lt;code&gt;index.html&lt;/code&gt; page using &lt;code&gt;nano&lt;/code&gt; or your favorite editor:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nano /var/www/&lt;span class="highlight"&gt;example.com&lt;/span&gt;/html/index.html
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Inside, add the following sample HTML:&lt;/p&gt;
&lt;div class="code-label " title="/var/www/example.com/html/index.html"&gt;/var/www/example.com/html/index.html&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&amp;lt;html&amp;gt;
    &amp;lt;head&amp;gt;
        &amp;lt;title&amp;gt;Welcome to &lt;span class="highlight"&gt;Example.com&lt;/span&gt;!&amp;lt;/title&amp;gt;
    &amp;lt;/head&amp;gt;
    &amp;lt;body&amp;gt;
        &amp;lt;h1&amp;gt;Success!  The &lt;span class="highlight"&gt;example.com&lt;/span&gt; server block is working!&amp;lt;/h1&amp;gt;
    &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save and close the file when you are finished.&lt;/p&gt;

&lt;p&gt;In order for Apache to serve this content, it's necessary to create a virtual host file with the correct directives. Instead of modifying the default configuration file located at &lt;code&gt;/etc/apache2/sites-available/000-default.conf&lt;/code&gt; directly, let's make a new one at &lt;code&gt;/etc/apache2/sites-available/&lt;span class="highlight"&gt;example.com&lt;/span&gt;.conf&lt;/code&gt;: &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/apache2/sites-available/&lt;span class="highlight"&gt;example.com&lt;/span&gt;.conf
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Paste in the following configuration block, which is similar to the default, but updated for our new directory and domain name:&lt;/p&gt;
&lt;div class="code-label " title="/etc/apache2/sites-available/example.com.conf"&gt;/etc/apache2/sites-available/example.com.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&amp;lt;VirtualHost *:80&amp;gt;
    ServerAdmin &lt;span class="highlight"&gt;admin@example.com&lt;/span&gt;
    ServerName &lt;span class="highlight"&gt;example.com&lt;/span&gt;
    ServerAlias &lt;span class="highlight"&gt;www.example.com&lt;/span&gt;
    DocumentRoot /var/www/&lt;span class="highlight"&gt;example.com&lt;/span&gt;/html
    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined
&amp;lt;/VirtualHost&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notice that we've updated the &lt;code&gt;DocumentRoot&lt;/code&gt; to our new directory and &lt;code&gt;ServerAdmin&lt;/code&gt; to an email that the &lt;strong&gt;example.com&lt;/strong&gt; site administrator can access. We've also added two directives: &lt;code&gt;ServerName&lt;/code&gt;, which establishes the base domain that should match for this virtual host definition, and &lt;code&gt;ServerAlias&lt;/code&gt;, which defines further names that should match as if they were the base name. &lt;/p&gt;

&lt;p&gt;Save and close the file when you are finished.&lt;/p&gt;

&lt;p&gt;Let's enable the file with the &lt;code&gt;a2ensite&lt;/code&gt; tool: &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo a2ensite &lt;span class="highlight"&gt;example.com&lt;/span&gt;.conf
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Disable the default site defined in &lt;code&gt;000-default.conf&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo a2dissite 000-default.conf
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, let's test for configuration errors: &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apache2ctl configtest
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see the following output:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Syntax OK
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Restart Apache to implement your changes:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl restart apache2
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Apache should now be serving your domain name. You can test this by navigating to &lt;code&gt;http://&lt;span class="highlight"&gt;example.com&lt;/span&gt;&lt;/code&gt;, where you should see something like this:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/apache_virt_hosts_1404/example.png" alt="Apache virtual host example"&gt;&lt;/p&gt;

&lt;h2 id="step-6-–-getting-familiar-with-important-apache-files-and-directories"&gt;Step 6 – Getting Familiar with Important Apache Files and Directories&lt;/h2&gt;

&lt;p&gt;Now that you know how to manage the Apache service itself, you should take a few minutes to familiarize yourself with a few important directories and files.&lt;/p&gt;

&lt;h3 id="content"&gt;Content&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/var/www/html&lt;/code&gt;: The actual web content, which by default only consists of the default Apache page you saw earlier, is served out of the &lt;code&gt;/var/www/html&lt;/code&gt; directory.  This can be changed by altering Apache configuration files.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id="server-configuration"&gt;Server Configuration&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/etc/apache2&lt;/code&gt;: The Apache configuration directory.  All of the Apache configuration files reside here.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/etc/apache2/apache2.conf&lt;/code&gt;: The main Apache configuration file.  This can be modified to make changes to the Apache global configuration.  This file is responsible for loading many of the other files in the configuration directory.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/etc/apache2/ports.conf&lt;/code&gt;: This file specifies the ports that Apache will listen on.  By default, Apache listens on port 80 and additionally listens on port 443 when a module providing SSL capabilities is enabled.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/etc/apache2/sites-available/&lt;/code&gt;: The directory where per-site virtual hosts can be stored.  Apache will not use the configuration files found in this directory unless they are linked to the &lt;code&gt;sites-enabled&lt;/code&gt; directory.  Typically, all server block configuration is done in this directory, and then enabled by linking to the other directory with the &lt;code&gt;a2ensite&lt;/code&gt; command.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/etc/apache2/sites-enabled/&lt;/code&gt;: The directory where enabled per-site virtual hosts are stored.  Typically, these are created by linking to configuration files found in the &lt;code&gt;sites-available&lt;/code&gt; directory with the &lt;code&gt;a2ensite&lt;/code&gt;.  Apache reads the configuration files and links found in this directory when it starts or reloads to compile a complete configuration.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/etc/apache2/conf-available/&lt;/code&gt;, &lt;code&gt;/etc/apache2/conf-enabled/&lt;/code&gt;: These directories have the same relationship as the &lt;code&gt;sites-available&lt;/code&gt; and &lt;code&gt;sites-enabled&lt;/code&gt; directories, but are used to store configuration fragments that do not belong in a virtual host.  Files in the &lt;code&gt;conf-available&lt;/code&gt; directory can be enabled with the &lt;code&gt;a2enconf&lt;/code&gt; command and disabled with the &lt;code&gt;a2disconf&lt;/code&gt; command.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/etc/apache2/mods-available/&lt;/code&gt;, &lt;code&gt;/etc/apache2/mods-enabled/&lt;/code&gt;: These directories contain the available and enabled modules, respectively.  Files in ending in &lt;code&gt;.load&lt;/code&gt; contain fragments to load specific modules, while files ending in &lt;code&gt;.conf&lt;/code&gt; contain the configuration for those modules.  Modules can be enabled and disabled using the &lt;code&gt;a2enmod&lt;/code&gt; and &lt;code&gt;a2dismod&lt;/code&gt; command.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id="server-logs"&gt;Server Logs&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/var/log/apache2/access.log&lt;/code&gt;: By default, every request to your web server is recorded in this log file unless Apache is configured to do otherwise.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/var/log/apache2/error.log&lt;/code&gt;: By default, all errors are recorded in this file.  The &lt;code&gt;LogLevel&lt;/code&gt; directive in the Apache configuration specifies how much detail the error logs will contain.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Now that you have your web server installed, you have many options for the type of content you can serve and the technologies you can use to create a richer experience.&lt;/p&gt;

&lt;p&gt;If you'd like to build out a more complete application stack, you can look at this article on &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-linux-apache-mariadb-php-lamp-stack-debian9"&gt;how to configure a LAMP stack on Debian 9&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-install-mongodb-on-debian-9</id>
    <published>2018-09-05T19:43:46Z</published>
    <updated>2018-09-05T19:45:12Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-install-mongodb-on-debian-9"/>
    <title>How to Install MongoDB on Debian 9</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.mongodb.com/"&gt;MongoDB&lt;/a&gt; is a free and open-source NoSQL document database used commonly in modern web applications.&lt;/p&gt;

&lt;p&gt;In this tutorial you will install MongoDB, manage its service, and optionally enable remote access.&lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;To follow this tutorial, you will need one Debian 9 server set up by following this &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-debian-9"&gt;initial server setup tutorial&lt;/a&gt;, including a sudo-enabled non-root user and a firewall.&lt;/p&gt;

&lt;h2 id="step-1-—-installing-mongodb"&gt;Step 1 — Installing MongoDB&lt;/h2&gt;

&lt;p&gt;Debian 9's official package repositories include a slightly-out-of-date version of MongoDB, which means we'll install from the official MongoDB repo instead.&lt;/p&gt;

&lt;p&gt;First, we need to add the MongoDB signing key with &lt;code&gt;apt-key add&lt;/code&gt;. We'll need to make sure the &lt;code&gt;curl&lt;/code&gt; command is installed before doing so:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install curl
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next we download the key and pass it to &lt;code&gt;apt-key add&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;curl https://www.mongodb.org/static/pgp/server-4.0.asc | sudo apt-key add -
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next we'll create a source list for the MongoDB repo, so &lt;code&gt;apt&lt;/code&gt; knows where to download from. First open the source list file in a text editor:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/apt/sources.list.d/mongodb-org-4.0.list
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will open a new blank file. Paste in the following:&lt;/p&gt;
&lt;div class="code-label " title="/etc/apt/sources.list.d/mongodb-org-4.0.list"&gt;/etc/apt/sources.list.d/mongodb-org-4.0.list&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;deb http://repo.mongodb.org/apt/debian stretch/mongodb-org/4.0 main
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save and close the file, then update your package cache:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt update
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Install the &lt;code&gt;mongodb-org&lt;/code&gt; package to install the server and some supporting tools:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt-get install mongodb-org
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, enable and start the &lt;code&gt;mongod&lt;/code&gt; service to get your MongoDB database running:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl enable mongod
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl start mongod
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We've now installed and started the latest stable version of MongoDB, along with helpful management tools for the MongoDB server.&lt;/p&gt;

&lt;p&gt;Next, let's verify that the server is running and works correctly.&lt;/p&gt;

&lt;h2 id="step-2-—-checking-the-service-and-database"&gt;Step 2 — Checking the Service and Database&lt;/h2&gt;

&lt;p&gt;We started MongoDB service in the previous step, now let's verify that it is started and the database is working.&lt;/p&gt;

&lt;p&gt;First, check the service's status:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl status mongod
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You'll see this output:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;● &lt;span class="highlight"&gt;mongod.service&lt;/span&gt; - MongoDB Database Server
   Loaded: loaded (/lib/systemd/system/mongod.service; enabled; vendor preset: enabled)
   Active: &lt;span class="highlight"&gt;active (running)&lt;/span&gt; since Wed 2018-09-05 16:59:56 UTC; 3s ago
     Docs: https://docs.mongodb.org/manual
 Main PID: 4321 (mongod)
    Tasks: 26
   CGroup: /system.slice/mongod.service
           └─4321 /usr/bin/mongod --config /etc/mongod.conf
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;According to &lt;code&gt;systemd&lt;/code&gt;, the MongoDB server is up and running.&lt;/p&gt;

&lt;p&gt;We can verify this further by actually connecting to the database server and executing a diagnostic command&lt;/p&gt;

&lt;p&gt;Execute this command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;mongo --eval 'db.runCommand({ connectionStatus: 1 })'
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will output the current database version, the server address and port, and the output of the status command:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;MongoDB shell version v4.0.2
connecting to: &lt;span class="highlight"&gt;mongodb://127.0.0.1:27017&lt;/span&gt;
MongoDB server version: 4.0.2
{
    "authInfo" : {
        "authenticatedUsers" : [ ],
        "authenticatedUserRoles" : [ ]
    },
    &lt;span class="highlight"&gt;"ok" : 1&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A value of &lt;code&gt;1&lt;/code&gt; for the &lt;code&gt;ok&lt;/code&gt; field in the response indicates that the server is working properly.&lt;/p&gt;

&lt;p&gt;Next, we'll look at how to manage the server instance.&lt;/p&gt;

&lt;h2 id="step-3-—-managing-the-mongodb-service"&gt;Step 3 — Managing the MongoDB Service&lt;/h2&gt;

&lt;p&gt;MongoDB installs as a systemd service, which means that you can manage it using standard &lt;code&gt;systemd&lt;/code&gt; commands alongside all other system services in Ubuntu.&lt;/p&gt;

&lt;p&gt;To verify the status of the service, type:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl status mongod
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can stop the server anytime by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl stop mongod
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To start the server when it is stopped, type:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl start mongod
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can also restart the server with a single command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl restart mongod
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the previous step we enabled MongoDB to start automatically with the server. If you wish to disable the automatic startup, type:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl disable mongod
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It's just as easy to enable it again. To do this, use:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl enable mongod
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, let's adjust the firewall settings for our MongoDB installation.&lt;/p&gt;

&lt;h2 id="step-4-—-adjusting-the-firewall-optional"&gt;Step 4 — Adjusting the Firewall (Optional)&lt;/h2&gt;

&lt;p&gt;Assuming you have followed the &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-debian-9"&gt;initial server setup tutorial&lt;/a&gt; instructions to enable the firewall on your server, the MongoDB server will be inaccessible from the internet.&lt;/p&gt;

&lt;p&gt;If you intend to use the MongoDB server only locally with applications running on the same server, this is the recommended and secure setting. However, if you would like to be able to connect to your MongoDB server from the internet, you have to allow the incoming connections in &lt;code&gt;ufw&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To allow access to MongoDB on its default port &lt;code&gt;27017&lt;/code&gt; from everywhere, you could use &lt;code&gt;sudo ufw allow &lt;span class="highlight"&gt;27017&lt;/span&gt;&lt;/code&gt;. However, enabling internet access to MongoDB server on a default installation gives anyone unrestricted access to the database server and its data.&lt;/p&gt;

&lt;p&gt;In most cases, MongoDB should be accessed only from certain trusted locations, such as another server hosting an application. To accomplish this task, you can allow access on MongoDB's default port while specifying the IP address of another server that will be explicitly allowed to connect:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw allow from &lt;span class="highlight"&gt;your_other_server_ip&lt;/span&gt;/32 to any port &lt;span class="highlight"&gt;27017&lt;/span&gt;  
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can verify the change in firewall settings with &lt;code&gt;ufw&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw status
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see traffic to port &lt;code&gt;27017&lt;/code&gt; allowed in the output:&lt;/p&gt;
&lt;div class="code-label " title="Output"&gt;Output&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;Status: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere
&lt;span class="highlight"&gt;27017                      ALLOW       Anywhere&lt;/span&gt;
OpenSSH (v6)               ALLOW       Anywhere (v6)
&lt;span class="highlight"&gt;27017 (v6)                 ALLOW       Anywhere (v6)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you have decided to allow only a certain IP address to connect to MongoDB server, the IP address of the allowed location will be listed instead of &lt;code&gt;Anywhere&lt;/code&gt; in the output.&lt;/p&gt;

&lt;p&gt;You can find more advanced firewall settings for restricting access to services in &lt;a href="https://www.digitalocean.com/community/tutorials/ufw-essentials-common-firewall-rules-and-commands"&gt;UFW Essentials: Common Firewall Rules and Commands&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Even though the port is open, MongoDB is currently only listening on the local address &lt;code&gt;127.0.0.1&lt;/code&gt;. To allow remote connections, add your server's publicly-routable IP address to the &lt;code&gt;mongod.conf&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Open the MongoDB configuration file in your editor:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/mongod.conf
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add your server's IP address to the &lt;code&gt;bindIP&lt;/code&gt; value:&lt;/p&gt;
&lt;div class="code-label " title="/etc/mongod.conf"&gt;/etc/mongod.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .
# network interfaces
net:
  port: 27017
  bindIp: 127.0.0.1&lt;span class="highlight"&gt;,your_server_ip&lt;/span&gt;
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Be sure to place a comma between the existing IP address and the one you added.&lt;/p&gt;

&lt;p&gt;Save the file, exit the editor, and restart MongoDB:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl restart mongod
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;MongoDB is now listening for remote connections, but anyone can access it. Follow Part 2 of &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-and-secure-mongodb-on-ubuntu-16-04#part-two-securing-mongodb"&gt;How to Install and Secure MongoDB on Ubuntu 16.04&lt;/a&gt; to add an administrative user and lock things down further.&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;You can find more in-depth tutorials on how to configure and use MongoDB in &lt;a href="https://www.digitalocean.com/community/search?q=mongodb"&gt;these DigitalOcean community articles&lt;/a&gt;. The official &lt;a href="https://docs.mongodb.com/v3.2/"&gt;MongoDB documentation&lt;/a&gt; is also a great resource on the possibilities that MongoDB provides.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-secure-nginx-with-let-s-encrypt-on-debian-9</id>
    <published>2018-09-05T17:30:09Z</published>
    <updated>2018-09-06T16:56:41Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-secure-nginx-with-let-s-encrypt-on-debian-9"/>
    <title>How To Secure Nginx with Let's Encrypt on Debian 9</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;Let's Encrypt is a Certificate Authority (CA) that provides an easy way to obtain and install free &lt;a href="https://www.digitalocean.com/community/tutorials/openssl-essentials-working-with-ssl-certificates-private-keys-and-csrs"&gt;TLS/SSL certificates&lt;/a&gt;, enabling encrypted HTTPS on web servers. It simplifies the process by providing a software client, Certbot, that attempts to automate most (if not all) of the required steps. Currently, the entire process of obtaining and installing a certificate is fully automated on both Apache and Nginx.&lt;/p&gt;

&lt;p&gt;In this tutorial, you will use Certbot to obtain a free SSL certificate for Nginx on Debian 9 and set up your certificate to renew automatically.&lt;/p&gt;

&lt;p&gt;This tutorial will use a separate Nginx server block file instead of the default file. &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-nginx-on-debian-9#step-5-%E2%80%93-setting-up-server-blocks"&gt;We recommend&lt;/a&gt; creating new Nginx server block files for each domain because it helps to avoid common mistakes and maintains the default files as a fallback configuration. &lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;To follow this tutorial, you will need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One Debian 9 server, set up by following this &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-debian-9"&gt;initial server setup for Debian 9&lt;/a&gt; tutorial, along with a sudo non-root user and a firewall.&lt;/li&gt;
&lt;li&gt;A fully registered domain name. This tutorial will use &lt;strong&gt;example.com&lt;/strong&gt; throughout. You can purchase a domain name on &lt;a href="https://namecheap.com"&gt;Namecheap&lt;/a&gt;, get one for free on &lt;a href="http://www.freenom.com/en/index.html"&gt;Freenom&lt;/a&gt;, or use the domain registrar of your choice.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Both of the following DNS records set up for your server. You can follow &lt;a href="https://www.digitalocean.com/docs/networking/dns/"&gt;this introduction to DigitalOcean DNS&lt;/a&gt; for details on how to add them.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An A record with &lt;code&gt;&lt;span class="highlight"&gt;example.com&lt;/span&gt;&lt;/code&gt; pointing to your server's public IP address.&lt;/li&gt;
&lt;li&gt;An A record with &lt;code&gt;www.&lt;span class="highlight"&gt;example.com&lt;/span&gt;&lt;/code&gt; pointing to your server's public IP address.&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Nginx installed by following &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-nginx-on-debian-9"&gt;How To Install Nginx on Debian 9&lt;/a&gt;. Be sure that you have a &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-nginx-on-debian-9#step-5-%E2%80%93-setting-up-server-blocks"&gt;server block&lt;/a&gt; for your domain. This tutorial will use &lt;code&gt;/etc/nginx/sites-available/&lt;span class="highlight"&gt;example.com&lt;/span&gt;&lt;/code&gt; as an example.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="step-1-—-installing-certbot"&gt;Step 1 — Installing Certbot&lt;/h2&gt;

&lt;p&gt;The first step to using Let's Encrypt to obtain an SSL certificate is to install the Certbot software on your server.&lt;/p&gt;

&lt;p&gt;Certbot is in very active development, so the Certbot packages provided by Debian with current stable releases tend to be outdated. However, we can obtain a more up-to-date package by enabling the Debian 9 backports repository in &lt;code&gt;/etc/apt/sources.list&lt;/code&gt;, where the &lt;code&gt;apt&lt;/code&gt; package manager looks for package sources. The backports repository includes recompiled packages that can be run without new libraries on stable Debian distributions.&lt;/p&gt;

&lt;p&gt;To add the backports repository, first open &lt;code&gt;/etc/apt/sources.list&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/apt/sources.list
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At the bottom of the file, add the following mirrors from the Debian project:&lt;/p&gt;
&lt;div class="code-label " title="/etc/apt/sources.list"&gt;/etc/apt/sources.list&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;...
deb http://deb.debian.org/debian stretch-backports main contrib non-free
deb-src http://deb.debian.org/debian stretch-backports main contrib non-free
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This includes the &lt;code&gt;main&lt;/code&gt; packages, which are &lt;a href="https://www.debian.org/social_contract#guidelines"&gt;Debian Free Software Guidelines (DFSG)&lt;/a&gt;- compliant, as well as the &lt;code&gt;non-free&lt;/code&gt; and &lt;code&gt;contrib&lt;/code&gt; components, which are either not DFSG-compliant themselves or include dependencies in this category.&lt;/p&gt;

&lt;p&gt;Save and close the file when you are finished.&lt;/p&gt;

&lt;p&gt;Update the package list to pick up the new repository's package information:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt update
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And finally, install Certbot's Nginx package with &lt;code&gt;apt&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install python-certbot-nginx -t stretch-backports
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Certbot is now ready to use, but in order for it to configure SSL for Nginx, we need to verify some of Nginx's configuration.&lt;/p&gt;

&lt;h2 id="step-2-—-confirming-nginx-39-s-configuration"&gt;Step 2 — Confirming Nginx's Configuration&lt;/h2&gt;

&lt;p&gt;Certbot needs to be able to find the correct &lt;code&gt;server&lt;/code&gt; block in your Nginx configuration for it to be able to automatically configure SSL. Specifically, it does this by looking for a &lt;code&gt;server_name&lt;/code&gt; directive that matches your requested domain.&lt;/p&gt;

&lt;p&gt;If you followed the &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-nginx-on-debian-9#step-5-%E2%80%93-setting-up-server-blocks"&gt;server block setup step in the Nginx installation tutorial&lt;/a&gt;, you should have a server block for your domain at &lt;code&gt;/etc/nginx/sites-available/&lt;span class="highlight"&gt;example.com&lt;/span&gt;&lt;/code&gt; with the &lt;code&gt;server_name&lt;/code&gt; directive already set appropriately.&lt;/p&gt;

&lt;p&gt;To check, open the server block file for your domain using &lt;code&gt;nano&lt;/code&gt; or your favorite text editor:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/nginx/sites-available/&lt;span class="highlight"&gt;example.com&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Find the existing &lt;code&gt;server_name&lt;/code&gt; line. It should look like this:&lt;/p&gt;
&lt;div class="code-label " title="/etc/nginx/sites-available/example.com"&gt;/etc/nginx/sites-available/example.com&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-nginx"&gt;...
server_name &lt;span class="highlight"&gt;example.com&lt;/span&gt; www.&lt;span class="highlight"&gt;example.com&lt;/span&gt;;
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If it does, exit your editor and move on to the next step.&lt;/p&gt;

&lt;p&gt;If it doesn't, update it to match. Then save the file, quit your editor, and verify the syntax of your configuration edits:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nginx -t
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you get an error, reopen the server block file and check for any typos or missing characters. Once your configuration file syntax is correct, reload Nginx to load the new configuration:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl reload nginx
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Certbot can now find the correct &lt;code&gt;server&lt;/code&gt; block and update it.&lt;/p&gt;

&lt;p&gt;Next, let's update the firewall to allow HTTPS traffic.&lt;/p&gt;

&lt;h2 id="step-3-—-allowing-https-through-the-firewall"&gt;Step 3 — Allowing HTTPS Through the Firewall&lt;/h2&gt;

&lt;p&gt;If you have the &lt;code&gt;ufw&lt;/code&gt; firewall enabled, as recommended in the prerequisite guides, you'll need to adjust the settings to allow for HTTPS traffic. &lt;/p&gt;

&lt;p&gt;You can see the current setting by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw status
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It will probably look like this, meaning that only HTTP traffic is allowed to the web server:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Status: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere                  
Nginx HTTP                 ALLOW       Anywhere                  
OpenSSH (v6)               ALLOW       Anywhere (v6)             
Nginx HTTP (v6)            ALLOW       Anywhere (v6)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To let in HTTPS traffic, allow the Nginx Full profile and delete the redundant Nginx HTTP profile allowance:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw allow 'Nginx Full'
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo ufw delete allow 'Nginx HTTP'
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Your status should now look like this:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw status
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Status: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere
Nginx Full                 ALLOW       Anywhere
OpenSSH (v6)               ALLOW       Anywhere (v6)
Nginx Full (v6)            ALLOW       Anywhere (v6)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, let's run Certbot and fetch our certificates.&lt;/p&gt;

&lt;h2 id="step-4-—-obtaining-an-ssl-certificate"&gt;Step 4 — Obtaining an SSL Certificate&lt;/h2&gt;

&lt;p&gt;Certbot provides a variety of ways to obtain SSL certificates through plugins. The Nginx plugin will take care of reconfiguring Nginx and reloading the config whenever necessary. To use this plugin, type the following:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo certbot --nginx -d &lt;span class="highlight"&gt;example.com&lt;/span&gt; -d &lt;span class="highlight"&gt;www.example.com&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This runs &lt;code&gt;certbot&lt;/code&gt; with the &lt;code&gt;--nginx&lt;/code&gt; plugin, using &lt;code&gt;-d&lt;/code&gt; to specify the names we'd like the certificate to be valid for.&lt;/p&gt;

&lt;p&gt;If this is your first time running &lt;code&gt;certbot&lt;/code&gt;, you will be prompted to enter an email address and agree to the terms of service. After doing so, &lt;code&gt;certbot&lt;/code&gt; will communicate with the Let's Encrypt server, then run a challenge to verify that you control the domain you're requesting a certificate for.&lt;/p&gt;

&lt;p&gt;If that's successful, &lt;code&gt;certbot&lt;/code&gt; will ask how you'd like to configure your HTTPS settings.&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Please choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access.
-------------------------------------------------------------------------------
1: No redirect - Make no further changes to the webserver configuration.
2: Redirect - Make all requests redirect to secure HTTPS access. Choose this for
new sites, or if you're confident your site works on HTTPS. You can undo this
change by editing your web server's configuration.
-------------------------------------------------------------------------------
Select the appropriate number [1-2] then [enter] (press 'c' to cancel):
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Select your choice then hit &lt;code&gt;ENTER&lt;/code&gt;. The configuration will be updated, and Nginx will reload to pick up the new settings. &lt;code&gt;certbot&lt;/code&gt; will wrap up with a message telling you the process was successful and where your certificates are stored:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/&lt;span class="highlight"&gt;example.com&lt;/span&gt;/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/&lt;span class="highlight"&gt;example.com&lt;/span&gt;/privkey.pem
   Your cert will expire on 2018-07-23. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot again
   with the "certonly" option. To non-interactively renew *all* of
   your certificates, run "certbot renew"
 - Your account credentials have been saved in your Certbot
   configuration directory at /etc/letsencrypt. You should make a
   secure backup of this folder now. This configuration directory will
   also contain certificates and private keys obtained by Certbot so
   making regular backups of this folder is ideal.
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Your certificates are downloaded, installed, and loaded. Try reloading your website using &lt;code&gt;https://&lt;/code&gt; and notice your browser's security indicator. It should indicate that the site is properly secured, usually with a green lock icon. If you test your server using the &lt;a href="https://www.ssllabs.com/ssltest/"&gt;SSL Labs Server Test&lt;/a&gt;, it will get an &lt;strong&gt;A&lt;/strong&gt; grade.&lt;/p&gt;

&lt;p&gt;Let's finish by testing the renewal process.&lt;/p&gt;

&lt;h2 id="step-5-—-verifying-certbot-auto-renewal"&gt;Step 5 — Verifying Certbot Auto-Renewal&lt;/h2&gt;

&lt;p&gt;Let's Encrypt's certificates are only valid for ninety days. This is to encourage users to automate their certificate renewal process. The &lt;code&gt;certbot&lt;/code&gt; package we installed takes care of this for us by adding a renew script to &lt;code&gt;/etc/cron.d&lt;/code&gt;. This script runs twice a day and will automatically renew any certificate that's within thirty days of expiration.&lt;/p&gt;

&lt;p&gt;To test the renewal process, you can do a dry run with &lt;code&gt;certbot&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo certbot renew --dry-run
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you see no errors, you're all set. When necessary, Certbot will renew your certificates and reload Nginx to pick up the changes. If the automated renewal process ever fails, Let’s Encrypt will send a message to the email you specified, warning you when your certificate is about to expire.&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;In this tutorial, you installed the Let's Encrypt client &lt;code&gt;certbot&lt;/code&gt;, downloaded SSL certificates for your domain, configured Nginx to use these certificates, and set up automatic certificate renewal. If you have further questions about using Certbot, &lt;a href="https://certbot.eff.org/docs/"&gt;their documentation&lt;/a&gt; is a good place to start.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-install-and-configure-vnc-on-debian-9</id>
    <published>2018-09-05T17:42:39Z</published>
    <updated>2018-09-05T17:42:50Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-install-and-configure-vnc-on-debian-9"/>
    <title>How to Install and Configure VNC on Debian 9</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;Virtual Network Computing&lt;/em&gt;, or VNC, is a connection system that allows you to use your keyboard and mouse to interact with a graphical desktop environment on a remote server. It makes managing files, software, and settings on a remote server easier for users who are not yet comfortable with the command line.&lt;/p&gt;

&lt;p&gt;In this guide, you'll set up a VNC server on a Debian 9 server and connect to it securely through an SSH tunnel. You'll use &lt;a href="https://www.tightvnc.com/"&gt;TightVNC&lt;/a&gt;, a fast and lightweight remote control package. This choice will ensure that our VNC connection will be smooth and stable even on slower internet connections.&lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;To complete this tutorial, you'll need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One Debian 9 server set up by following &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-debian-9"&gt;the Debian 9 initial server setup guide&lt;/a&gt;, including a non-root user with &lt;code&gt;sudo&lt;/code&gt; access and a firewall. &lt;/li&gt;
&lt;li&gt;A local computer with a VNC client installed that supports VNC connections over SSH tunnels. 

&lt;ul&gt;
&lt;li&gt;On Winows, you can use &lt;a href="https://www.tightvnc.com/"&gt;TightVNC&lt;/a&gt;, &lt;a href="https://www.realvnc.com/"&gt;RealVNC&lt;/a&gt;, or &lt;a href="https://www.uvnc.com/"&gt;UltraVNC&lt;/a&gt;. &lt;/li&gt;
&lt;li&gt;On macOS, you can use the built-in &lt;a href="https://support.apple.com/guide/mac-help/screen-sharing-overview-mh14066/mac"&gt;Screen Sharing&lt;/a&gt; program, or can use a cross-platform app like &lt;a href="https://www.realvnc.com/"&gt;RealVNC&lt;/a&gt;. &lt;/li&gt;
&lt;li&gt;On Linux, you can choose from many options, including  &lt;code&gt;vinagre&lt;/code&gt;, &lt;code&gt;krdc&lt;/code&gt;, &lt;a href="https://www.realvnc.com/"&gt;RealVNC&lt;/a&gt;, or &lt;a href="https://www.tightvnc.com/"&gt;TightVNC&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="step-1-—-installing-the-desktop-environment-and-vnc-server"&gt;Step 1 — Installing the Desktop Environment and VNC Server&lt;/h2&gt;

&lt;p&gt;By default, a Debian 9 server does not come with a graphical desktop environment or a VNC server installed, so we'll begin by installing those. Specifically, we will install packages for the latest &lt;a href="https://xfce.org/"&gt;Xfce&lt;/a&gt; desktop environment and the TightVNC package available in the official Debian repository.&lt;/p&gt;

&lt;p&gt;On your server, update your list of packages:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt update
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now  install the Xfce desktop environment on your server:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install xfce4 xfce4-goodies
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;During the installation, you'll be prompted to select your keyboard layout from a list of possible options. Choose the one that's appropriate for your language and press &lt;code&gt;Enter&lt;/code&gt;. The installation will continue.&lt;/p&gt;

&lt;p&gt;Once that installation completes, install the TightVNC server:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install tightvncserver
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To complete the VNC server's initial configuration after installation, use the &lt;code&gt;vncserver&lt;/code&gt; command to set up a secure password and create the initial configuration files:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;vncserver
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You'll be prompted to enter and verify a password to access your machine remotely:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;You will require a password to access your desktops.

Password:
Verify:
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The password must be between six and eight characters long. Passwords more than 8 characters will be truncated automatically.&lt;/p&gt;

&lt;p&gt;Once you verify the password, you'll have the option to create a a view-only password. Users who log in with the view-only password will not be able to control the VNC instance with their mouse or keyboard. This is a helpful option if you want to demonstrate something to other people using your VNC server, but this isn't required.&lt;/p&gt;

&lt;p&gt;The process then creates the necessary default configuration files and connection information for the server:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Would you like to enter a view-only password (y/n)? &lt;span class="highlight"&gt;n&lt;/span&gt;
xauth:  file /home/&lt;span class="highlight"&gt;sammy&lt;/span&gt;/.Xauthority does not exist

New 'X' desktop is &lt;span class="highlight"&gt;your_hostname&lt;/span&gt;:1

Creating default startup script /home/&lt;span class="highlight"&gt;sammy&lt;/span&gt;/.vnc/xstartup
Starting applications specified in /home/&lt;span class="highlight"&gt;sammy&lt;/span&gt;/.vnc/xstartup
Log file is /home/&lt;span class="highlight"&gt;sammy&lt;/span&gt;/.vnc/&lt;span class="highlight"&gt;your_hostname&lt;/span&gt;:1.log
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now let's configure the VNC server.&lt;/p&gt;

&lt;h2 id="step-2-—-configuring-the-vnc-server"&gt;Step 2 — Configuring the VNC Server&lt;/h2&gt;

&lt;p&gt;The VNC server needs to know which commands to execute when it starts up. Specifically, VNC needs to know which graphical desktop it should connect to.&lt;/p&gt;

&lt;p&gt;These commands are located in a configuration file called &lt;code&gt;xstartup&lt;/code&gt; in the &lt;code&gt;.vnc&lt;/code&gt; folder under your home directory. The startup script was created when you ran the &lt;code&gt;vncserver&lt;/code&gt; in the previous step, but we'll create our own to launch the Xfce desktop.&lt;/p&gt;

&lt;p&gt;When VNC is first set up, it launches a default server instance on port &lt;code&gt;5901&lt;/code&gt;. This port is called a &lt;em&gt;display port&lt;/em&gt;, and is referred to by VNC as &lt;code&gt;:1&lt;/code&gt;. VNC can launch multiple instances on other display ports, like &lt;code&gt;:2&lt;/code&gt;, &lt;code&gt;:3&lt;/code&gt;, and so on.&lt;/p&gt;

&lt;p&gt;Because we are going to be changing how the VNC server is configured, first stop the VNC server instance that is running on port &lt;code&gt;5901&lt;/code&gt; with the following command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;vncserver -kill :1
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The output should look like this, although you'll see a different PID:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Killing Xtightvnc process ID &lt;span class="highlight"&gt;17648&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Before you modify the &lt;code&gt;xstartup&lt;/code&gt; file,  back up the original:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;mv ~/.vnc/xstartup ~/.vnc/xstartup.bak
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now create a new &lt;code&gt;xstartup&lt;/code&gt; file and open it in your text editor:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nano ~/.vnc/xstartup
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Commands in this file are executed  automatically whenever you start or restart the VNC server. We need VNC to start our desktop environment if it's not already started.  Add these commands to the file:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="~/.vnc/xstartup"&gt;~/.vnc/xstartup&lt;/div&gt;#!/bin/bash
xrdb $HOME/.Xresources
startxfce4 &amp;amp;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first command in the file, &lt;code&gt;xrdb $HOME/.Xresources&lt;/code&gt;, tells VNC's GUI framework to read the server user's &lt;code&gt;.Xresources&lt;/code&gt; file. &lt;code&gt;.Xresources&lt;/code&gt; is where a user can make changes to certain settings of the graphical desktop, like terminal colors, cursor themes, and font rendering. The second command  tells the server to launch Xfce, which is where you will find all of the graphical software that you need to comfortably manage your server.&lt;/p&gt;

&lt;p&gt;To ensure that the VNC server will be able to use this new startup file properly, we'll need to make it executable.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo chmod +x ~/.vnc/xstartup
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, restart the VNC server.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;vncserver
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You'll see output similar to this:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;New 'X' desktop is &lt;span class="highlight"&gt;your_hostname&lt;/span&gt;:1

Starting applications specified in /home/&lt;span class="highlight"&gt;sammy&lt;/span&gt;/.vnc/xstartup
Log file is /home/&lt;span class="highlight"&gt;sammy&lt;/span&gt;/.vnc/&lt;span class="highlight"&gt;your_hostname&lt;/span&gt;:1.log
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With the configuration in place, let's connect to the server from our local machine.&lt;/p&gt;

&lt;h2 id="step-3-—-connecting-the-vnc-desktop-securely"&gt;Step 3 — Connecting the VNC Desktop Securely&lt;/h2&gt;

&lt;p&gt;VNC itself doesn't use secure protocols when connecting. We'll use an SSH tunnel to connect securely to our server, and then tell our VNC client to use that tunnel rather than making a direct connection.&lt;/p&gt;

&lt;p&gt;Create an SSH connection on your local computer that securely forwards to the &lt;code&gt;localhost&lt;/code&gt; connection for VNC. You can do this via the terminal on Linux or macOS with the following command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;ssh -L &lt;span class="highlight"&gt;5901&lt;/span&gt;:127.0.0.1:&lt;span class="highlight"&gt;5901&lt;/span&gt; -C -N -l &lt;span class="highlight"&gt;sammy&lt;/span&gt; &lt;span class="highlight"&gt;your_server_ip&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;-L&lt;/code&gt; switch specifies the port bindings. In this case we're binding port &lt;code&gt;5901&lt;/code&gt; of the remote connection to port &lt;code&gt;5901&lt;/code&gt; on your local machine. The &lt;code&gt;-C&lt;/code&gt; switch enables compression, while the &lt;code&gt;-N&lt;/code&gt; switch tells &lt;code&gt;ssh&lt;/code&gt; that we don't want to execute a remote command. The &lt;code&gt;-l&lt;/code&gt; switch specifies the remote login name.&lt;/p&gt;

&lt;p&gt;Remember to replace &lt;code&gt;&lt;span class="highlight"&gt;sammy&lt;/span&gt;&lt;/code&gt; and &lt;code&gt;&lt;span class="highlight"&gt;your_server_ip&lt;/span&gt;&lt;/code&gt; with the sudo non-root username and IP address of your server.&lt;/p&gt;

&lt;p&gt;If you are using a graphical SSH client, like PuTTY, use &lt;code&gt;&lt;span class="highlight"&gt;your_server_ip&lt;/span&gt;&lt;/code&gt; as the connection IP, and set &lt;code&gt;localhost:5901&lt;/code&gt; as a new forwarded port in the program's SSH tunnel settings.&lt;/p&gt;

&lt;p&gt;Once the tunnel is running, use a VNC client to connect to &lt;code&gt;localhost:5901&lt;/code&gt;. You'll be prompted to authenticate using the password you set in Step 1.&lt;/p&gt;

&lt;p&gt;Once you are connected, you'll see the default Xfce desktop. &lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/vnc_debian9/qOf5NgG.png" alt="VNC connection to Debian 9 server"&gt;Select &lt;strong&gt;Use default config&lt;/strong&gt; to configure your desktop quickly.&lt;/p&gt;

&lt;p&gt;You can access files in your home directory with the file manager or from the command line, as seen here:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/vnc_debian9/j6HsSCr.png" alt="Files via VNC connection to Debian 9"&gt;&lt;/p&gt;

&lt;p&gt;On your local machine, press &lt;code&gt;CTRL+C&lt;/code&gt; in your terminal to stop the SSH tunnel and return to your prompt. This will disconnect your VNC session as well.&lt;/p&gt;

&lt;p&gt;Next let's set up the VNC server as a service.&lt;/p&gt;

&lt;h2 id="step-4-—-running-vnc-as-a-system-service"&gt;Step 4 — Running VNC as a System Service&lt;/h2&gt;

&lt;p&gt;Next, we'll set up the VNC server as a systemd service so we can start, stop, and restart it as needed, like any other  service. This will also ensure that VNC starts up when your server reboots.&lt;/p&gt;

&lt;p&gt;First, create a new unit file called &lt;code&gt;/etc/systemd/system/vncserver@.service&lt;/code&gt; using your favorite text editor:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/systemd/system/vncserver@.service
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;@&lt;/code&gt; symbol at the end of the name will let us pass in an argument we can use in the service configuration. We'll use this to specify the VNC display port we want to use when we manage the service.&lt;/p&gt;

&lt;p&gt;Add the following lines to the file. Be sure to change the value of &lt;strong&gt;User&lt;/strong&gt;, &lt;strong&gt;Group&lt;/strong&gt;, &lt;strong&gt;WorkingDirectory&lt;/strong&gt;,  and the username in the value of &lt;strong&gt;PIDFILE&lt;/strong&gt; to match your username:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="/etc/systemd/system/vncserver@.service "&gt;/etc/systemd/system/vncserver@.service &lt;/div&gt;[Unit]
Description=Start TightVNC server at startup
After=syslog.target network.target

[Service]
Type=forking
User=&lt;span class="highlight"&gt;sammy&lt;/span&gt;
Group=&lt;span class="highlight"&gt;sammy&lt;/span&gt;
WorkingDirectory=/home/&lt;span class="highlight"&gt;sammy&lt;/span&gt;

PIDFile=/home/&lt;span class="highlight"&gt;sammy&lt;/span&gt;/.vnc/%H:%i.pid
ExecStartPre=-/usr/bin/vncserver -kill :%i &amp;gt; /dev/null 2&amp;gt;&amp;amp;1
ExecStart=/usr/bin/vncserver &lt;span class="highlight"&gt;-depth 24 -geometry 1280x800&lt;/span&gt; :%i
ExecStop=/usr/bin/vncserver -kill :%i

[Install]
WantedBy=multi-user.target
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;ExecStartPre&lt;/code&gt; command stops VNC if it's already running. The &lt;code&gt;ExecStart&lt;/code&gt; command starts VNC and sets the color depth to 24-bit color with a resolution of 1280x800. You can modify these startup options as well to meet your needs.&lt;/p&gt;

&lt;p&gt;Save and close the file.&lt;/p&gt;

&lt;p&gt;Next, make the system aware of the new unit file.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl daemon-reload
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Enable the unit file.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl enable vncserver@1.service
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;1&lt;/code&gt; following the &lt;code&gt;@&lt;/code&gt; sign signifies which display number the service should appear over, in this case the default &lt;code&gt;:1&lt;/code&gt; as was discussed in Step 2.. &lt;/p&gt;

&lt;p&gt;Stop the current instance of the VNC server if it's still running.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;vncserver -kill :1
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then start it as you would start any other systemd service.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl start vncserver@1
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can verify that it started with this command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl status vncserver@1
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If it started correctly, the output should look like this:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;● vncserver@1.service - Start TightVNC server at startup
   Loaded: loaded (/etc/systemd/system/vncserver@.service; enabled; vendor preset: enabled)
   Active: &lt;span class="highlight"&gt;active&lt;/span&gt; (running) since Wed 2018-09-05 16:47:40 UTC; 3s ago
  Process: 4977 ExecStart=/usr/bin/vncserver -depth 24 -geometry 1280x800 :1 (code=exited, status=0/SUCCESS)
  Process: 4971 ExecStartPre=/usr/bin/vncserver -kill :1 &amp;gt; /dev/null 2&amp;gt;&amp;amp;1 (code=exited, status=0/SUCCESS)
 Main PID: 4987 (Xtightvnc)

...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Your VNC server will now be available when you reboot the machine.&lt;/p&gt;

&lt;p&gt;Start your SSH tunnel again:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;ssh -L &lt;span class="highlight"&gt;5901&lt;/span&gt;:127.0.0.1:&lt;span class="highlight"&gt;5901&lt;/span&gt; -C -N -l &lt;span class="highlight"&gt;sammy&lt;/span&gt; &lt;span class="highlight"&gt;your_server_ip&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then make a new connection using your VNC client software to &lt;code&gt;localhost:5901&lt;/code&gt; to connect to your machine. &lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;You now have a secured VNC server up and running on your Debian 9 server. Now you'll be able to manage your files, software, and settings with an easy-to-use and familiar graphical interface, and you'll be able to run graphical software like web browsers remotely.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-install-the-latest-mysql-on-debian-9</id>
    <published>2018-09-05T16:30:26Z</published>
    <updated>2018-09-17T14:35:06Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-install-the-latest-mysql-on-debian-9"/>
    <title>How To Install the Latest MySQL on Debian 9</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.mysql.com/"&gt;MySQL&lt;/a&gt; is a prominent open source database management system used to store and retrieve data for a wide variety of popular applications. MySQL is the &lt;strong&gt;M&lt;/strong&gt; in the &lt;em&gt;LAMP&lt;/em&gt; stack, a commonly used set of open source software that also includes Linux, the Apache web server, and the PHP programming language.&lt;/p&gt;

&lt;p&gt;In Debian 9, MariaDB, a community fork of the MySQL project, is packaged as the default MySQL variant.  While, MariaDB works well in most cases, if you need features found only in Oracle's MySQL, you can install and use packages from a repository maintained by the MySQL developers.&lt;/p&gt;

&lt;p&gt;To install the latest version of MySQL, we'll add this repository, install the MySQL software itself, secure the install, and finally we'll test that MySQL is running and responding to commands.&lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;Before starting this tutorial, you will need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One Debian 9 server set up by following &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-debian-9"&gt;this initial server setup guide&lt;/a&gt;, including a non-&lt;strong&gt;root&lt;/strong&gt; user with &lt;code&gt;sudo&lt;/code&gt; privileges and a firewall.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="step-1-—-adding-the-mysql-software-repository"&gt;Step 1 — Adding the MySQL Software Repository&lt;/h2&gt;

&lt;p&gt;The MySQL developers provide a &lt;code&gt;.deb&lt;/code&gt; package that handles configuring and installing the official MySQL software repositories.  Once the repositories are set up, we'll be able to use Ubuntu's standard &lt;code&gt;apt&lt;/code&gt; command to install the software. We'll download this &lt;code&gt;.deb&lt;/code&gt; file with &lt;code&gt;wget&lt;/code&gt; and then install it with the &lt;code&gt;dpkg&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;First, load &lt;a href="https://dev.mysql.com/downloads/repo/apt/"&gt;the MySQL download page&lt;/a&gt; in your web browser.  Find the &lt;strong&gt;Download&lt;/strong&gt; button in the lower-right corner and click through to the next page.  This page will prompt you to log in or sign up for an Oracle web account.  We can skip that and instead look for the link that says &lt;strong&gt;No thanks, just start my download&lt;/strong&gt;.  Right-click the link and select &lt;strong&gt;Copy Link Address&lt;/strong&gt; (this option may be worded differently, depending on your browser).&lt;/p&gt;

&lt;p&gt;Now we're going to download the file.  On your server, move to a directory you can write to.  Download the file using &lt;code&gt;wget&lt;/code&gt;, remembering to paste the address you just copied in place of the highlighted portion below:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cd /tmp
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;wget &lt;span class="highlight"&gt;https://dev.mysql.com/get/mysql-apt-config_0.8.10-1_all.deb&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The file should now be downloaded in our current directory.  List the files to make sure:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;ls
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see the filename listed:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;mysql-apt-config_&lt;span class="highlight"&gt;0.8.10-1&lt;/span&gt;_all.deb
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we're ready to install:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo dpkg -i mysql-apt-config*
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;dpkg&lt;/code&gt; is used to install, remove, and inspect &lt;code&gt;.deb&lt;/code&gt; software packages.  The &lt;code&gt;-i&lt;/code&gt; flag indicates that we'd like to install from the specified file.&lt;/p&gt;

&lt;p&gt;During the installation, you'll be presented with a configuration screen where you can specify which version of MySQL you'd prefer, along with an option to install repositories for other MySQL-related tools.  The defaults will add the repository information for the latest stable version of MySQL and nothing else.  This is what we want, so use the down arrow to navigate to the &lt;code&gt;Ok&lt;/code&gt; menu option and hit &lt;code&gt;ENTER&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The package will now finish adding the repository.  Refresh your &lt;code&gt;apt&lt;/code&gt; package cache to make the new software packages available:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt update
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that we've added the MySQL repositories, we're ready to install the actual MySQL server software.  If you ever need to update the configuration of these repositories, just run &lt;code&gt;sudo dpkg-reconfigure mysql-apt-config&lt;/code&gt;, select new options, and then &lt;code&gt;sudo apt-get update&lt;/code&gt; to refresh your package cache.&lt;/p&gt;

&lt;h2 id="step-2-—-installing-mysql"&gt;Step 2 — Installing MySQL&lt;/h2&gt;

&lt;p&gt;Having added the repository and with our package cache freshly updated, we can now use &lt;code&gt;apt&lt;/code&gt; to install the latest MySQL server package:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install mysql-server
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;apt&lt;/code&gt; will look at all available &lt;code&gt;mysql-server&lt;/code&gt; packages and determine that the MySQL provided package is the newest and best candidate.  It will then calculate package dependencies and ask you to approve the installation.  Type &lt;code&gt;y&lt;/code&gt; then &lt;code&gt;ENTER&lt;/code&gt;.  The software will install.&lt;/p&gt;

&lt;p&gt;You will be asked to set a &lt;strong&gt;root&lt;/strong&gt; password during the configuration phase of the installation.  Choose and confirm a secure password to continue.  Next, a prompt will appear asking for you to select a default authentication plugin.  Read the display to understand the choices.  If you are not sure, choosing &lt;strong&gt;Use Strong Password Encryption&lt;/strong&gt; is safer.&lt;/p&gt;

&lt;p&gt;MySQL should be installed and running now.  Let's check using &lt;code&gt;systemctl&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl status mysql
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;● mysql.service - MySQL Community Server
   Loaded: loaded (/lib/systemd/system/mysql.service; enabled; vendor preset: enabled)
   &lt;span class="highlight"&gt;Active: active (running)&lt;/span&gt; since Wed 2018-09-05 15:58:21 UTC; 30s ago
     Docs: man:mysqld(8)
           http://dev.mysql.com/doc/refman/en/using-systemd.html
 Main PID: 12805 (mysqld)
   Status: "SERVER_OPERATING"
   CGroup: /system.slice/mysql.service
           └─12805 /usr/sbin/mysqld

Sep 05 15:58:15 mysql1 systemd[1]: Starting MySQL Community Server...
Sep 05 15:58:21 mysql1 systemd[1]: &lt;span class="highlight"&gt;Started MySQL Community Server.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;Active: active (running)&lt;/code&gt; line means MySQL is installed and running.  Now we'll make the installation a little more secure.&lt;/p&gt;

&lt;h2 id="step-3-—-securing-mysql"&gt;Step 3 — Securing MySQL&lt;/h2&gt;

&lt;p&gt;MySQL comes with a command we can use to perform a few security-related updates on our new install.  Let's run it now:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;mysql_secure_installation
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will ask you for the MySQL &lt;strong&gt;root&lt;/strong&gt; password that you set during installation. Type it in and press &lt;code&gt;ENTER&lt;/code&gt;.  Now we'll answer a series of yes or no prompts. Let's go through them:&lt;/p&gt;

&lt;p&gt;First, we are asked about the &lt;strong&gt;validate password plugin&lt;/strong&gt;, a plugin that can automatically enforce certain password strength rules for your MySQL users.  Enabling this is a decision you'll need to make based on your individual security needs.  Type &lt;code&gt;y&lt;/code&gt; and &lt;code&gt;ENTER&lt;/code&gt; to enable it, or just hit &lt;code&gt;ENTER&lt;/code&gt; to skip it.  If enabled, you will also be prompted to choose a level from 0–2 for how strict the password validation will be.  Choose a number and hit &lt;code&gt;ENTER&lt;/code&gt; to continue.&lt;/p&gt;

&lt;p&gt;Next you'll be asked if you want to change the &lt;strong&gt;root&lt;/strong&gt; password.  Since we just created the password when we installed MySQL, we can safely skip this.  Hit &lt;code&gt;ENTER&lt;/code&gt; to continue without updating the password.&lt;/p&gt;

&lt;p&gt;The rest of the prompts can be answered &lt;strong&gt;yes&lt;/strong&gt;.  You will be asked about removing the &lt;strong&gt;anonymous&lt;/strong&gt; MySQL user, disallowing remote &lt;strong&gt;root&lt;/strong&gt; login, removing the &lt;strong&gt;test&lt;/strong&gt; database, and reloading privilege tables to ensure the previous changes take effect properly.  These are all a good idea. Type &lt;code&gt;y&lt;/code&gt; and hit &lt;code&gt;ENTER&lt;/code&gt; for each.&lt;/p&gt;

&lt;p&gt;The script will exit after all the prompts are answered.  Now our MySQL installation is reasonably secured.  Let's test it again by running a client that connects to the server and returns some information.&lt;/p&gt;

&lt;h2 id="step-4-–-testing-mysql"&gt;Step 4 – Testing MySQL&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;mysqladmin&lt;/code&gt; is a command line administrative client for MySQL.  We'll use it to connect to the server and output some version and status information:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;mysqladmin -u root -p version
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;-u root&lt;/code&gt; portion tells &lt;code&gt;mysqladmin&lt;/code&gt; to log in as the MySQL &lt;strong&gt;root&lt;/strong&gt; user, &lt;code&gt;-p&lt;/code&gt; instructs the client to ask for a password, and &lt;code&gt;version&lt;/code&gt; is the actual command we want to run.&lt;/p&gt;

&lt;p&gt;The output will let us know what version of the MySQL server is running, its uptime, and some other status information:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;mysqladmin  Ver 8.0.12 for Linux on x86_64 (MySQL Community Server - GPL)
Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Server version      8.0.12
Protocol version    10
Connection      Localhost via UNIX socket
UNIX socket     /var/run/mysqld/mysqld.sock
Uptime:         6 min 42 sec

Threads: 2  Questions: 12  Slow queries: 0  Opens: 123  Flush tables: 2  Open tables: 99  Queries per second avg: 0.029
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you received similar output, congrats! You've successfully installed the latest MySQL server and secured it.&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;You've now completed a basic install of the latest version of MySQL, which should work for many popular applications.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-install-python-3-and-set-up-a-programming-environment-on-debian-9</id>
    <published>2018-09-04T22:12:59Z</published>
    <updated>2018-09-04T22:13:23Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-install-python-3-and-set-up-a-programming-environment-on-debian-9"/>
    <title>How To Install Python 3 and Set Up a Programming Environment on Debian 9</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;A flexible and versatile programming language, Python is effective for many use cases, including scripting, automation, data analysis, machine learning, and back-end development. First published in 1991 with a name inspired by the British comedy group Monty Python, the development team wanted to make Python a language that was fun to use. Quick to set up, and written in a relatively straightforward style with immediate feedback on errors, Python is a great choice for beginners and experienced developers alike. &lt;a href="https://www.digitalocean.com/community/tutorials/python-2-vs-python-3-practical-considerations-2"&gt;Python 3 is the most current version&lt;/a&gt; of the language and is considered to be the future of Python.  &lt;/p&gt;

&lt;p&gt;This tutorial will get your Debian 9 server set up with a Python 3 programming environment. Programming on a server has many advantages and supports collaboration across development projects. &lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;In order to complete this tutorial, you should have a non-root user with &lt;code&gt;sudo&lt;/code&gt; privileges on a Debian 9 server. To learn how to achieve this setup, follow our &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-debian-9"&gt;Debian 9 initial server setup guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you’re not already familiar with a terminal environment, you may find the article “&lt;a href="https://www.digitalocean.com/community/tutorials/an-introduction-to-the-linux-terminal"&gt;An Introduction to the Linux Terminal&lt;/a&gt;” useful for becoming better oriented with the terminal.&lt;/p&gt;

&lt;p&gt;With your server and user set up, you are ready to begin.&lt;/p&gt;

&lt;h2 id="step-1-—-setting-up-python-3"&gt;Step 1 — Setting Up Python 3&lt;/h2&gt;

&lt;p&gt;Debian Linux ships with both Python 3 and Python 2 pre-installed. To make sure that our versions are up-to-date, let’s update and upgrade the system with the &lt;code&gt;apt&lt;/code&gt; command to work with the &lt;strong&gt;A&lt;/strong&gt;dvanced &lt;strong&gt;P&lt;/strong&gt;ackaging &lt;strong&gt;T&lt;/strong&gt;ool:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt update
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo apt -y upgrade
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;-y&lt;/code&gt; flag will confirm that we are agreeing for all items to be installed. &lt;/p&gt;

&lt;p&gt;Once the process is complete, we can check the version of Python 3 that is installed in the system by typing: &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;python3 -V
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You’ll receive output in the terminal window that will let you know the version number. While this number may vary, the output will be similar to this:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Python &lt;span class="highlight"&gt;3.5.3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To manage software packages for Python, let’s install &lt;strong&gt;pip&lt;/strong&gt;, a tool that will install and manage programming packages we may want to use in our development projects. You can learn more about modules or packages that you can install with pip by reading “&lt;a href="https://www.digitalocean.com/community/tutorials/how-to-import-modules-in-python-3"&gt;How To Import Modules in Python 3&lt;/a&gt;.”&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install -y python3-pip
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Python packages can be installed by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;pip3 install &lt;span class="highlight"&gt;package_name&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, &lt;code&gt;&lt;span class="highlight"&gt;package_name&lt;/span&gt;&lt;/code&gt; can refer to any Python package or library, such as Django for web development or NumPy for scientific computing. So if you would like to install NumPy, you can do so with the command &lt;code&gt;pip3 install numpy&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;There are a few more packages and development tools to install to ensure that we have a robust set-up for our programming environment:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install build-essential libssl-dev libffi-dev python3-dev
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once Python is set up, and pip and other tools are installed, we can set up a virtual environment for our development projects.&lt;/p&gt;

&lt;h2 id="step-2-—-setting-up-a-virtual-environment"&gt;Step 2 —  Setting Up a Virtual Environment&lt;/h2&gt;

&lt;p&gt;Virtual environments enable you to have an isolated space on your server for Python projects, ensuring that each of your projects can have its own set of dependencies that won’t disrupt any of your other projects.&lt;/p&gt;

&lt;p&gt;Setting up a programming environment provides us with greater control over our Python projects and over how different versions of packages are handled. This is especially important when working with third-party packages. &lt;/p&gt;

&lt;p&gt;You can set up as many Python programming environments as you want. Each environment is basically a directory or folder on your server that has a few scripts in it to make it act as an environment. &lt;/p&gt;

&lt;p&gt;While there are a few ways to achieve a programming environment in Python, we’ll be using the &lt;strong&gt;venv&lt;/strong&gt; module here, which is part of the standard Python 3 library. Let’s install venv by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install -y python3-venv
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With this installed, we are ready to create environments. Let’s either choose which directory we would like to put our Python programming environments in, or create a new directory with &lt;code&gt;mkdir&lt;/code&gt;, as in:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;mkdir &lt;span class="highlight"&gt;environments&lt;/span&gt;
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;cd &lt;span class="highlight"&gt;environments&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once you are in the directory where you would like the environments to live, you can create an environment by running the following command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;python3.5 -m venv &lt;span class="highlight"&gt;my_env&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Essentially, &lt;code&gt;pyvenv&lt;/code&gt; sets up a new directory that contains a few items which we can view with the &lt;code&gt;ls&lt;/code&gt; command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;ls my_env
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;bin include lib lib64 pyvenv.cfg share
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Together, these files work to make sure that your projects are isolated from the broader context of your local machine, so that system files and project files don’t mix. This is good practice for version control and to ensure that each of your projects has access to the particular packages that it needs. Python Wheels, a built-package format for Python that can speed up your software production by reducing the number of times you need to compile, will be in the Ubuntu 18.04 &lt;code&gt;share&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;To use this environment, you need to activate it, which you can achieve by typing the following command that calls the &lt;strong&gt;activate&lt;/strong&gt; script:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;source &lt;span class="highlight"&gt;my_env&lt;/span&gt;/bin/activate
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Your command prompt will now be prefixed with the name of your environment, in this case it is called &lt;span class="highlight"&gt;my_env&lt;/span&gt;. Depending on what version of Debian Linux you are running, your prefix may appear somewhat differently, but the name of your environment in parentheses should be the first thing you see on your line:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="(my_env) sammy@ubuntu:~/environments$"&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This prefix lets us know that the environment &lt;span class="highlight"&gt;my_env&lt;/span&gt; is currently active, meaning that when we create programs here they will use only this particular environment’s settings and packages. &lt;/p&gt;

&lt;p&gt;&lt;span class='note'&gt;&lt;strong&gt;Note:&lt;/strong&gt; Within the virtual environment, you can use the command &lt;code&gt;python&lt;/code&gt; instead of &lt;code&gt;python3&lt;/code&gt;, and &lt;code&gt;pip&lt;/code&gt; instead of &lt;code&gt;pip3&lt;/code&gt; if you would prefer. If you use Python 3 on your machine outside of an environment, you will need to use the &lt;code&gt;python3&lt;/code&gt; and &lt;code&gt;pip3&lt;/code&gt; commands exclusively. &lt;br&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;After following these steps, your virtual environment is ready to use.&lt;/p&gt;

&lt;h2 id="step-3-—-creating-a-“hello-world”-program"&gt;Step 3 — Creating a “Hello, World” Program&lt;/h2&gt;

&lt;p&gt;Now that we have our virtual environment set up, let’s create a traditional “Hello, World!” program. This will let us test our environment and provides us with the opportunity to become more familiar with Python if we aren’t already.&lt;/p&gt;

&lt;p&gt;To do this, we’ll open up a command-line text editor such as nano and create a new file:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="(my_env) sammy@ubuntu:~/environments$"&gt;nano hello.py
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once the text file opens up in the terminal window we’ll type out our program:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-python"&gt;print("Hello, World!")
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Exit nano by typing the &lt;code&gt;CTRL&lt;/code&gt; and &lt;code&gt;X&lt;/code&gt; keys, and when prompted to save the file press &lt;code&gt;y&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Once you exit out of nano and return to your shell, let’s run the program:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="(my_env) sammy@ubuntu:~/environments$"&gt;python hello.py
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;hello.py&lt;/code&gt; program that you just created should cause your terminal to produce the following output:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Hello, World!
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To leave the environment, simply type the command &lt;code&gt;deactivate&lt;/code&gt; and you will return to your original directory. &lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Congratulations! At this point you have a Python 3 programming environment set up on your Debian 9 Linux server and you can now begin a coding project!&lt;/p&gt;

&lt;p&gt;If you are using a local machine rather than a server, refer to the tutorial that is relevant to your operating system in our “&lt;a href="https://www.digitalocean.com/community/tutorial_series/how-to-install-and-set-up-a-local-programming-environment-for-python-3"&gt;How To Install and Set Up a Local Programming Environment for Python 3&lt;/a&gt;” series.&lt;/p&gt;

&lt;p&gt;With your server ready for software development, you can continue to learn more about coding in Python by reading our free &lt;a href="https://do.co/python-book"&gt;&lt;em&gt;How To Code in Python 3&lt;/em&gt; eBook&lt;/a&gt;, or consulting our &lt;a href="https://www.digitalocean.com/community/tags/project/tutorials"&gt;Programming Project tutorials&lt;/a&gt;. &lt;/p&gt;

&lt;div class='code-label notes-and-warnings note' title='&lt;strong&gt;Download our free Python eBook!&lt;/strong&gt;'&gt;&lt;strong&gt;Download our free Python eBook!&lt;/strong&gt;&lt;/div&gt;&lt;span class='note'&gt;&lt;p&gt;

&lt;a href="https://do.co/python-book-epub"&gt;&lt;em&gt;How To Code in Python&lt;/em&gt; eBook in &lt;strong&gt;EPUB format&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://do.co/python-book-pdf"&gt;&lt;em&gt;How To Code in Python&lt;/em&gt; eBook in &lt;strong&gt;PDF format&lt;/strong&gt;&lt;/a&gt;&lt;br&gt;&lt;/p&gt;&lt;/span&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-install-and-use-docker-on-debian-9</id>
    <published>2018-09-04T21:06:54Z</published>
    <updated>2018-10-19T20:42:21Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-install-and-use-docker-on-debian-9"/>
    <title>How to Install and Use Docker on Debian 9</title>
    <content type="html">&lt;p&gt;&lt;em&gt;A previous version of this tutorial was written by &lt;a href="https://www.digitalocean.com/community/users/finid"&gt;finid&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.docker.com/"&gt;Docker&lt;/a&gt; is an application that simplifies the process of managing application processes in &lt;em&gt;containers&lt;/em&gt;.  Containers let you run your applications in resource-isolated processes. They're similar to virtual machines, but containers are more portable, more resource-friendly, and more dependent on the host operating system. &lt;/p&gt;

&lt;p&gt;For a detailed introduction to the different components of a Docker container, check out &lt;a href="https://www.digitalocean.com/community/tutorials/the-docker-ecosystem-an-introduction-to-common-components"&gt;The Docker Ecosystem: An Introduction to Common Components&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this tutorial, you'll install and use Docker Community Edition (CE) on Debian 9. You'll install Docker itself, work with containers and images, and push an image to a Docker Repository.&lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;To follow this tutorial, you will need the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One Debian 9 server set up by following &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-debian-9"&gt;the Debian 9 initial server setup guide&lt;/a&gt;, including a sudo non-root user and a firewall.&lt;/li&gt;
&lt;li&gt;An account on &lt;a href="https://hub.docker.com/"&gt;Docker Hub&lt;/a&gt; if you wish to create your own images and push them to Docker Hub, as shown in Steps 7 and 8. &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="step-1-—-installing-docker"&gt;Step 1 — Installing Docker&lt;/h2&gt;

&lt;p&gt;The Docker installation package available in the official Debian repository may not be the latest version. To ensure we get the latest version, we'll install Docker from the official Docker repository. To do that, we'll add a new package source, add the GPG key from Docker to ensure the downloads are valid, and then install the package.&lt;/p&gt;

&lt;p&gt;First, update your existing list of packages:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt update
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, install a few prerequisite packages which let &lt;code&gt;apt&lt;/code&gt; use packages over HTTPS:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install apt-transport-https ca-certificates curl gnupg2 software-properties-common
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then add the GPG key for the official Docker repository to your system:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt-key add -
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add the Docker repository to APT sources:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/debian $(lsb_release -cs) stable"
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, update the package database with the Docker packages from the newly added repo:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt update
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Make sure you are about to install from the Docker repo instead of the default Debian repo:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;apt-cache policy docker-ce
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You'll see output like this, although the version number for Docker may be different:&lt;/p&gt;
&lt;div class="code-label " title="Output of apt-cache policy docker-ce"&gt;Output of apt-cache policy docker-ce&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;docker-ce:
  Installed: (none)
  Candidate: &lt;span class="highlight"&gt;18.06.1~ce~3-0~debian&lt;/span&gt;
  Version table:
     &lt;span class="highlight"&gt;18.06.1~ce~3-0~debian&lt;/span&gt; 500
        500 https://download.docker.com/linux/debian stretch/stable amd64 Packages
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notice that &lt;code&gt;docker-ce&lt;/code&gt; is not installed, but the candidate for installation is from the Docker repository for Debian 9 (&lt;code&gt;stretch&lt;/code&gt;). &lt;/p&gt;

&lt;p&gt;Finally, install Docker:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install docker-ce
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Docker should now be installed, the daemon started, and the process enabled to start on boot. Check that it's running:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl status docker
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The output should be similar to the following, showing that the service is active and running:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;● docker.service - Docker Application Container Engine
   Loaded: loaded (/lib/systemd/system/docker.service; enabled; vendor preset: enabled)
   Active: active (running) since Thu 2018-07-05 15:08:39 UTC; 2min 55s ago
     Docs: https://docs.docker.com
  Main PID: 21319 (dockerd)
   CGroup: /system.slice/docker.service
           ├─21319 /usr/bin/dockerd -H fd://
           └─21326 docker-containerd --config /var/run/docker/containerd/containerd.toml
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Installing Docker now gives you not just the Docker service (daemon) but also the &lt;code&gt;docker&lt;/code&gt; command line utility, or the Docker client. We'll explore how to use the &lt;code&gt;docker&lt;/code&gt; command later in this tutorial.&lt;/p&gt;

&lt;h2 id="step-2-—-executing-the-docker-command-without-sudo-optional"&gt;Step 2 — Executing the Docker Command Without Sudo (Optional)&lt;/h2&gt;

&lt;p&gt;By default, the &lt;code&gt;docker&lt;/code&gt; command can only be run the &lt;strong&gt;root&lt;/strong&gt; user or by a user in the &lt;strong&gt;docker&lt;/strong&gt; group, which is automatically created during Docker's installation process. If you attempt to run the &lt;code&gt;docker&lt;/code&gt; command without prefixing it with &lt;code&gt;sudo&lt;/code&gt; or without being in the &lt;strong&gt;docker&lt;/strong&gt; group, you'll get an output like this:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;docker: Cannot connect to the Docker daemon. Is the docker daemon running on this host?.
See 'docker run --help'.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you want to avoid typing &lt;code&gt;sudo&lt;/code&gt; whenever you run the &lt;code&gt;docker&lt;/code&gt; command, add your username to the &lt;code&gt;docker&lt;/code&gt; group:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo usermod -aG docker ${USER}
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To apply the new group membership,  log out of the server and back in, or type the following:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;su - ${USER}
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will be prompted to enter your user's password to continue.  &lt;/p&gt;

&lt;p&gt;Confirm that your user is now added to the &lt;strong&gt;docker&lt;/strong&gt; group by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;id -nG
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;&lt;span class="highlight"&gt;sammy&lt;/span&gt; sudo docker
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you need to add a user to the &lt;code&gt;docker&lt;/code&gt; group that you're not logged in as, declare that username explicitly using:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo usermod -aG docker &lt;span class="highlight"&gt;username&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The rest of this article assumes you are running the &lt;code&gt;docker&lt;/code&gt; command as a user in the &lt;strong&gt;docker&lt;/strong&gt; group. If you choose not to, please prepend the commands with &lt;code&gt;sudo&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let's explore the &lt;code&gt;docker&lt;/code&gt; command next.&lt;/p&gt;

&lt;h2 id="step-3-—-using-the-docker-command"&gt;Step 3 — Using the Docker Command&lt;/h2&gt;

&lt;p&gt;Using &lt;code&gt;docker&lt;/code&gt; consists of passing it a chain of options and commands followed by arguments. The syntax takes this form:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;docker [option] [command] [arguments]
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To view all available subcommands, type:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;docker
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As of Docker 18, the complete list of available subcommands includes:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;
  attach      Attach local standard input, output, and error streams to a running container
  build       Build an image from a Dockerfile
  commit      Create a new image from a container's changes
  cp          Copy files/folders between a container and the local filesystem
  create      Create a new container
  diff        Inspect changes to files or directories on a container's filesystem
  events      Get real time events from the server
  exec        Run a command in a running container
  export      Export a container's filesystem as a tar archive
  history     Show the history of an image
  images      List images
  import      Import the contents from a tarball to create a filesystem image
  info        Display system-wide information
  inspect     Return low-level information on Docker objects
  kill        Kill one or more running containers
  load        Load an image from a tar archive or STDIN
  login       Log in to a Docker registry
  logout      Log out from a Docker registry
  logs        Fetch the logs of a container
  pause       Pause all processes within one or more containers
  port        List port mappings or a specific mapping for the container
  ps          List containers
  pull        Pull an image or a repository from a registry
  push        Push an image or a repository to a registry
  rename      Rename a container
  restart     Restart one or more containers
  rm          Remove one or more containers
  rmi         Remove one or more images
  run         Run a command in a new container
  save        Save one or more images to a tar archive (streamed to STDOUT by default)
  search      Search the Docker Hub for images
  start       Start one or more stopped containers
  stats       Display a live stream of container(s) resource usage statistics
  stop        Stop one or more running containers
  tag         Create a tag TARGET_IMAGE that refers to SOURCE_IMAGE
  top         Display the running processes of a container
  unpause     Unpause all processes within one or more containers
  update      Update configuration of one or more containers
  version     Show the Docker version information
  wait        Block until one or more containers stop, then print their exit codes
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To view the options available to a specific command, type:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;docker &lt;span class="highlight"&gt;docker-subcommand&lt;/span&gt; --help
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To view system-wide information about Docker, use:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;docker info
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let's explore some of these commands. We'll start by working with images.&lt;/p&gt;

&lt;h2 id="step-4-—-working-with-docker-images"&gt;Step 4 — Working with Docker Images&lt;/h2&gt;

&lt;p&gt;Docker containers are built from Docker images. By default, Docker pulls these images from &lt;a href="https://hub.docker.com"&gt;Docker Hub&lt;/a&gt;, a Docker registry managed by Docker, the company behind the Docker project. Anyone can host their Docker images on Docker Hub, so most applications and Linux distributions you'll need will have images hosted there.&lt;/p&gt;

&lt;p&gt;To check whether you can access and download images from Docker Hub, type:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;docker run hello-world
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The output will indicate that Docker in working correctly:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
9db2ca6ccae0: Pull complete
Digest: sha256:4b8ff392a12ed9ea17784bd3c9a8b1fa3299cac44aca35a85c90c5e3c7afacdc
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Docker was initially unable to find the &lt;code&gt;hello-world&lt;/code&gt; image locally, so it downloaded the image from Docker Hub, which is the default repository. Once the image downloaded, Docker created a container from the image and the application within the container executed, displaying the message.&lt;/p&gt;

&lt;p&gt;You can search for images available on Docker Hub by using the &lt;code&gt;docker&lt;/code&gt; command with the &lt;code&gt;search&lt;/code&gt; subcommand. For example, to search for the Ubuntu image, type:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;docker search ubuntu
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The script will crawl Docker Hub and return a listing of all images whose name match the search string. In this case, the output will be similar to this:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;NAME                                                   DESCRIPTION                                     STARS               OFFICIAL            AUTOMATED
ubuntu                                                 Ubuntu is a Debian-based Linux operating sys…   8320                [OK]
dorowu/ubuntu-desktop-lxde-vnc                         Ubuntu with openssh-server and NoVNC            214                                     [OK]
rastasheep/ubuntu-sshd                                 Dockerized SSH service, built on top of offi…   170                                     [OK]
consol/ubuntu-xfce-vnc                                 Ubuntu container with "headless" VNC session…   128                                     [OK]
ansible/ubuntu14.04-ansible                            Ubuntu 14.04 LTS with ansible                   95                                      [OK]
ubuntu-upstart                                         Upstart is an event-based replacement for th…   88                  [OK]
neurodebian                                            NeuroDebian provides neuroscience research s…   53                  [OK]
1and1internet/ubuntu-16-nginx-php-phpmyadmin-mysql-5   ubuntu-16-nginx-php-phpmyadmin-mysql-5          43                                      [OK]
ubuntu-debootstrap                                     debootstrap --variant=minbase --components=m…   39                  [OK]
nuagebec/ubuntu                                        Simple always updated Ubuntu docker images w…   23                                      [OK]
tutum/ubuntu                                           Simple Ubuntu docker images with SSH access     18
i386/ubuntu                                            Ubuntu is a Debian-based Linux operating sys…   13
1and1internet/ubuntu-16-apache-php-7.0                 ubuntu-16-apache-php-7.0                        12                                      [OK]
ppc64le/ubuntu                                         Ubuntu is a Debian-based Linux operating sys…   12
eclipse/ubuntu_jdk8                                    Ubuntu, JDK8, Maven 3, git, curl, nmap, mc, …   6                                       [OK]
darksheer/ubuntu                                       Base Ubuntu Image -- Updated hourly             4                                       [OK]
codenvy/ubuntu_jdk8                                    Ubuntu, JDK8, Maven 3, git, curl, nmap, mc, …   4                                       [OK]
1and1internet/ubuntu-16-nginx-php-5.6-wordpress-4      ubuntu-16-nginx-php-5.6-wordpress-4             3                                       [OK]
pivotaldata/ubuntu                                     A quick freshening-up of the base Ubuntu doc…   2
1and1internet/ubuntu-16-sshd                           ubuntu-16-sshd                                  1                                       [OK]
ossobv/ubuntu                                          Custom ubuntu image from scratch (based on o…   0
smartentry/ubuntu                                      ubuntu with smartentry                          0                                       [OK]
1and1internet/ubuntu-16-healthcheck                    ubuntu-16-healthcheck                           0                                       [OK]
pivotaldata/ubuntu-gpdb-dev                            Ubuntu images for GPDB development              0
paasmule/bosh-tools-ubuntu                             Ubuntu based bosh-cli                           0                                       [OK]
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the &lt;strong&gt;OFFICIAL&lt;/strong&gt; column, &lt;strong&gt;OK&lt;/strong&gt; indicates an image built and supported by the company behind the project. Once you've identified the image that you would like to use, you can download it to your computer using the &lt;code&gt;pull&lt;/code&gt; subcommand.&lt;/p&gt;

&lt;p&gt;Execute the following command to download the official &lt;code&gt;ubuntu&lt;/code&gt; image to your computer:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;docker pull ubuntu
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You'll see the following output:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Using default tag: latest
latest: Pulling from library/ubuntu
6b98dfc16071: Pull complete
4001a1209541: Pull complete
6319fc68c576: Pull complete
b24603670dc3: Pull complete
97f170c87c6f: Pull complete
Digest: sha256:5f4bdc3467537cbbe563e80db2c3ec95d548a9145d64453b06939c4592d67b6d
Status: Downloaded newer image for ubuntu:latest
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After an image has been downloaded, you can then run a container using the downloaded image with the &lt;code&gt;run&lt;/code&gt; subcommand. As you saw with the &lt;code&gt;hello-world&lt;/code&gt; example, if an image has not been downloaded when &lt;code&gt;docker&lt;/code&gt; is executed with the &lt;code&gt;run&lt;/code&gt; subcommand, the Docker client will first download the image, then run a container using it.&lt;/p&gt;

&lt;p&gt;To see the images that have been downloaded to your computer, type:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;docker images
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The output should look similar to the following:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
ubuntu              latest              16508e5c265d        13 days ago         84.1MB
hello-world         latest              2cb0d9787c4d        7 weeks ago         1.85kB
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you'll see later in this tutorial, images that you use to run containers can be modified and used to generate new images, which may then be uploaded (&lt;em&gt;pushed&lt;/em&gt; is the technical term) to Docker Hub or other Docker registries.&lt;/p&gt;

&lt;p&gt;Let's look at how to run containers in more detail.&lt;/p&gt;

&lt;h2 id="step-5-—-running-a-docker-container"&gt;Step 5 — Running a Docker Container&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;hello-world&lt;/code&gt; container you ran in the previous step is an example of a container that runs and exits after emitting a test message. Containers can be much more useful than that, and they can be interactive. After all, they are similar to virtual machines, only more resource-friendly.&lt;/p&gt;

&lt;p&gt;As an example, let's run a container using the latest image of Ubuntu. The combination of the &lt;strong&gt;-i&lt;/strong&gt; and &lt;strong&gt;-t&lt;/strong&gt; switches gives you interactive shell access into the container:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;docker run -it ubuntu
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Your command prompt should change to reflect the fact that you're now working inside the container and should take this form:&lt;/p&gt;
&lt;pre class="code-pre  second-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;root@d9b100f2f636:/#
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note the container id in the command prompt. In this example, it is &lt;code&gt;d9b100f2f636&lt;/code&gt;. You'll need that container ID later to identify the container when you want to remove it.&lt;/p&gt;

&lt;p&gt;Now you can run any command inside the container. For example, let's update the package database inside the container. You don't need to prefix any command with &lt;code&gt;sudo&lt;/code&gt;, because you're operating inside the container as the &lt;strong&gt;root&lt;/strong&gt; user:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix second-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="root@d9b100f2f636:/#"&gt;apt update
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then install any application in it. Let's install Node.js:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix second-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="root@d9b100f2f636:/#"&gt;apt install nodejs
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This installs Node.js in the container from the official Ubuntu repository. When the installation finishes, verify that Node.js is installed:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix second-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="root@d9b100f2f636:/#"&gt;node -v
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You'll see the version number displayed in your terminal:&lt;/p&gt;
&lt;pre class="code-pre  second-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;v8.10.0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Any changes you make inside the container only apply to that container. &lt;/p&gt;

&lt;p&gt;To exit the container, type &lt;code&gt;exit&lt;/code&gt; at the prompt.&lt;/p&gt;

&lt;p&gt;Let's look at managing the containers on our system next.&lt;/p&gt;

&lt;h2 id="step-6-—-managing-docker-containers"&gt;Step 6 — Managing Docker Containers&lt;/h2&gt;

&lt;p&gt;After using Docker for a while, you'll have many active (running) and inactive containers on your computer. To view the &lt;strong&gt;active ones&lt;/strong&gt;, use:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;docker ps
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will see output similar to the following:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;CONTAINER ID        IMAGE               COMMAND             CREATED             

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this tutorial, you started two containers; one from the &lt;code&gt;hello-world&lt;/code&gt; image and another from the &lt;code&gt;ubuntu&lt;/code&gt; image. Both containers are no longer running, but they still exist on your system.&lt;/p&gt;

&lt;p&gt;To view all containers — active and inactive, run &lt;code&gt;docker ps&lt;/code&gt;  with the &lt;code&gt;-a&lt;/code&gt; switch:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;docker ps -a
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You'll see output similar to this:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;d9b100f2f636        ubuntu              "/bin/bash"         About an hour ago   Exited (0) 8 minutes ago                           sharp_volhard
01c950718166        hello-world         "/hello"            About an hour ago   Exited (0) About an hour ago                       festive_williams

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To view the latest container you created, pass it the &lt;code&gt;-l&lt;/code&gt; switch:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;docker ps -l
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                      PORTS               NAMES
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;d9b100f2f636        ubuntu              "/bin/bash"         About an hour ago   Exited (0) 10 minutes ago                       sharp_volhard
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To start a stopped container, use &lt;code&gt;docker start&lt;/code&gt;, followed by the container ID or the container's name.  Let's start the Ubuntu-based container with the ID of  &lt;code&gt;d9b100f2f636&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;docker start &lt;span class="highlight"&gt;d9b100f2f636&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The container will start, and you can use &lt;code&gt;docker ps&lt;/code&gt; to see its status:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
d9b100f2f636        ubuntu              "/bin/bash"         About an hour ago   Up 8 seconds                            sharp_volhard

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To stop a running container, use &lt;code&gt;docker stop&lt;/code&gt;, followed by the container ID or name. This time, we'll use the name that Docker assigned the container, which is &lt;code&gt;sharp_volhard&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;docker stop &lt;span class="highlight"&gt;sharp_volhard&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once you've decided you no longer need a container anymore, remove it with the &lt;code&gt;docker rm&lt;/code&gt; command, again using either the container ID or the name.  Use the &lt;code&gt;docker ps -a&lt;/code&gt; command to find the container ID or name for the container associated with the &lt;code&gt;hello-world&lt;/code&gt; image and remove it. &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;docker rm &lt;span class="highlight"&gt;festive_williams&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can start a new container and give it a name using the &lt;code&gt;--name&lt;/code&gt; switch. You can also use the &lt;code&gt;--rm&lt;/code&gt; switch to create a container that removes itself when it's stopped. See the &lt;code&gt;docker run help&lt;/code&gt; command for more information on these options and others.&lt;/p&gt;

&lt;p&gt;Containers can be turned into images which you can use to build new containers. Let's look at how that works.&lt;/p&gt;

&lt;h2 id="step-7-—-committing-changes-in-a-container-to-a-docker-image"&gt;Step 7 — Committing Changes in a Container to a Docker Image&lt;/h2&gt;

&lt;p&gt;When you start up a Docker image, you can create, modify, and delete files just like you can with a virtual machine. The changes that you make will only apply to that container. You can start and stop it, but once you destroy it with the &lt;code&gt;docker rm&lt;/code&gt; command, the changes will be lost for good. &lt;/p&gt;

&lt;p&gt;This section shows you how to save the state of a container as a new Docker image.&lt;/p&gt;

&lt;p&gt;After installing Node.js inside the Ubuntu container, you now have a container running off an image, but the container is different from the image you used to create it.  But you might want to reuse this Node.js container as the basis for new images later.&lt;/p&gt;

&lt;p&gt;Then commit the changes to a new Docker image instance using the following command. &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;docker commit -m "What you did to the image" -a "Author Name" &lt;span class="highlight"&gt;container_id&lt;/span&gt; &lt;span class="highlight"&gt;repository&lt;/span&gt;/&lt;span class="highlight"&gt;new_image_name&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;strong&gt;-m&lt;/strong&gt; switch is for the commit message that helps you and others know what changes you made, while &lt;strong&gt;-a&lt;/strong&gt; is used to specify the author. The &lt;code&gt;container_id&lt;/code&gt; is the one you noted earlier in the tutorial when you started the interactive Docker session. Unless you created additional repositories on Docker Hub, the &lt;code&gt;repository&lt;/code&gt; is usually your Docker Hub username.&lt;/p&gt;

&lt;p&gt;For example, for the user &lt;strong&gt;sammy&lt;/strong&gt;, with the container ID of &lt;code&gt;d9b100f2f636&lt;/code&gt;, the command would be:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;docker commit -m "added Node.js" -a "&lt;span class="highlight"&gt;sammy&lt;/span&gt;" &lt;span class="highlight"&gt;d9b100f2f636&lt;/span&gt; &lt;span class="highlight"&gt;sammy&lt;/span&gt;/ubuntu-nodejs
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When you &lt;em&gt;commit&lt;/em&gt; an image, the new image is saved locally on your computer. Later in this tutorial, you'll learn how to push an image to a Docker registry like Docker Hub so others can access it.&lt;/p&gt;

&lt;p&gt;Listing the Docker images again will show the new image, as well as the old one that it was derived from:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;docker images
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You'll see output like this:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;REPOSITORY               TAG                 IMAGE ID            CREATED             SIZE
&lt;span class="highlight"&gt;sammy&lt;/span&gt;/ubuntu-nodejs   latest              7c1f35226ca6        7 seconds ago       179MB
ubuntu                   latest              113a43faa138        4 weeks ago         81.2MB
hello-world              latest              e38bc07ac18e        2 months ago        1.85kB

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, &lt;code&gt;ubuntu-nodejs&lt;/code&gt; is the new image, which was derived from the existing &lt;code&gt;ubuntu&lt;/code&gt; image from Docker Hub. The size difference reflects the changes that were made. And in this example, the change was that NodeJS was installed. So next time you need to run a container using Ubuntu with NodeJS pre-installed, you can just use the new image. &lt;/p&gt;

&lt;p&gt;You can also build Images from a &lt;code&gt;Dockerfile&lt;/code&gt;, which lets you automate the installation of software in a new image. However, that's outside the scope of this tutorial.&lt;/p&gt;

&lt;p&gt;Now let's share the new image with others so they can create containers from it.&lt;/p&gt;

&lt;h2 id="step-8-—-pushing-docker-images-to-a-docker-repository"&gt;Step 8 — Pushing Docker Images to a Docker Repository&lt;/h2&gt;

&lt;p&gt;The next logical step after creating a new image from an existing image is to share it with a select few of your friends, the whole world on Docker Hub, or other Docker registry that you have access to. To push an image to Docker Hub or any other Docker registry, you must have an account there.&lt;/p&gt;

&lt;p&gt;This section shows you how to push a Docker image to Docker Hub. To learn how to create your own private Docker registry, check out &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-set-up-a-private-docker-registry-on-ubuntu-14-04"&gt;How To Set Up a Private Docker Registry on Ubuntu 14.04&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To push your image, first log into Docker Hub. &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;docker login -u &lt;span class="highlight"&gt;docker-registry-username&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You'll be prompted to authenticate using your Docker Hub password. If you specified the correct password, authentication should succeed. &lt;/p&gt;

&lt;span class='note'&gt;&lt;p&gt;
&lt;strong&gt;Note:&lt;/strong&gt; If your Docker registry username is different from the local username you used to create the image, you will have to tag your image with your registry username. For the example given in the last step, you would type:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;docker tag &lt;span class="highlight"&gt;sammy&lt;/span&gt;/ubuntu-nodejs &lt;span class="highlight"&gt;docker-registry-username&lt;/span&gt;/ubuntu-nodejs
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;/p&gt;&lt;/span&gt;

&lt;p&gt;Then you may push your own image using:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;docker push &lt;span class="highlight"&gt;docker-registry-username&lt;/span&gt;/&lt;span class="highlight"&gt;docker-image-name&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To push the &lt;code&gt;ubuntu-nodejs&lt;/code&gt; image to the &lt;strong&gt;sammy&lt;/strong&gt; repository, the command would be:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;docker push &lt;span class="highlight"&gt;sammy&lt;/span&gt;/&lt;span class="highlight"&gt;ubuntu-nodejs&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The process may take some time to complete as it uploads the images, but when completed, the output will look like this:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;The push refers to a repository [docker.io/&lt;span class="highlight"&gt;sammy&lt;/span&gt;/ubuntu-nodejs]
e3fbbfb44187: Pushed
5f70bf18a086: Pushed
a3b5c80a4eba: Pushed
7f18b442972b: Pushed
3ce512daaf78: Pushed
7aae4540b42d: Pushed

...


&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After pushing an image to a registry, it should be listed on your account's dashboard, like that show in the image below.&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/docker_1804/ec2vX3Z.png" alt="New Docker image listing on Docker Hub"&gt;&lt;/p&gt;

&lt;p&gt;If a push attempt results in an error of this sort, then you likely did not log in:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;The push refers to a repository [docker.io/&lt;span class="highlight"&gt;sammy&lt;/span&gt;/ubuntu-nodejs]
e3fbbfb44187: Preparing
5f70bf18a086: Preparing
a3b5c80a4eba: Preparing
7f18b442972b: Preparing
3ce512daaf78: Preparing
7aae4540b42d: Waiting
unauthorized: authentication required
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Log in with &lt;code&gt;docker login&lt;/code&gt; and repeat the push attempt. Then verify that it exists on your Docker Hub repository page.&lt;/p&gt;

&lt;p&gt;You can now use &lt;code&gt;docker pull &lt;span class="highlight"&gt;sammy&lt;/span&gt;/&lt;span class="highlight"&gt;ubuntu-nodejs&lt;/span&gt;&lt;/code&gt; to pull the image to a new machine and use it to run a new container.&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;In this tutorial you installed Docker, worked with images and containers, and pushed a modified image to Docker Hub. Now that you know the basics, explore the &lt;a href="https://www.digitalocean.com/community/tags/docker?type=tutorials"&gt;other Docker tutorials&lt;/a&gt; in the DigitalOcean Community.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-set-up-time-synchronization-on-debian-9</id>
    <published>2018-09-04T20:33:23Z</published>
    <updated>2018-09-04T20:34:09Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-set-up-time-synchronization-on-debian-9"/>
    <title>How To Set Up Time Synchronization on Debian 9</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;Accurate timekeeping has become a critical component of modern software deployments. Whether it's making sure logs are recorded in the right order or database updates are applied correctly, out-of-sync time can cause errors, data corruption, and other hard to debug issues.&lt;/p&gt;

&lt;p&gt;Debian 9 has time synchronization built in and activated by default using the standard ntpd time server, provided by the &lt;code&gt;ntp&lt;/code&gt; package. In this article we will look at some basic time-related commands, verify that ntpd is active and connected to peers, and learn how to activate the alternate systemd-timesyncd network time service.&lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;Before starting this tutorial, you will need a Debian 9 server with a non-root, sudo-enabled user, as described in &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-debian-9"&gt;this Debian 9 server setup tutorial&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id="navigating-basic-time-commands"&gt;Navigating Basic Time Commands&lt;/h2&gt;

&lt;p&gt;The most basic command for finding out the time on your server is &lt;code&gt;date&lt;/code&gt;. Any user can type this command to print out the date and time:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;date
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Tue Sep  4 17:51:49 &lt;span class="highlight"&gt;UTC&lt;/span&gt; 2018
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Most often your server will default to the &lt;em&gt;UTC&lt;/em&gt; time zone, as highlighted in the above output. UTC is &lt;em&gt;Coordinated Universal Time&lt;/em&gt;, the time at zero degrees longitude. Consistently using Universal Time reduces confusion when your infrastructure spans multiple time zones.&lt;/p&gt;

&lt;p&gt;If you have different requirements and need to change the time zone, you can use the &lt;code&gt;timedatectl&lt;/code&gt; command to do so.&lt;/p&gt;

&lt;p&gt;First, list the available time zones:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;timedatectl list-timezones
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A list of time zones will print to your screen. You can press &lt;code&gt;SPACE&lt;/code&gt; to page down, and &lt;code&gt;b&lt;/code&gt; to page up. Once you find the correct time zone, make note of it then type &lt;code&gt;q&lt;/code&gt; to exit the list.&lt;/p&gt;

&lt;p&gt;Now set the time zone with &lt;code&gt;timedatectl set-timezone&lt;/code&gt;, making sure to replace the highlighted portion below with the time zone you found in the list. You'll need to use &lt;code&gt;sudo&lt;/code&gt; with &lt;code&gt;timedatectl&lt;/code&gt; to make this change:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo timedatectl set-timezone &lt;span class="highlight"&gt;America/New_York&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can verify your changes by running &lt;code&gt;date&lt;/code&gt; again:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;date
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Tue Sep  4 13:52:57 &lt;span class="highlight"&gt;EDT&lt;/span&gt; 2018
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The time zone abbreviation should reflect the newly chosen value.&lt;/p&gt;

&lt;p&gt;Now that we know how to check the clock and set time zones, let’s make sure our time is being synchronized properly.&lt;/p&gt;

&lt;h2 id="checking-the-status-of-ntpd"&gt;Checking the Status of ntpd&lt;/h2&gt;

&lt;p&gt;By default, Debian 9 runs the standard ntpd server to keep your system time synchronized with a pool of external time servers. We can check that it's running with the &lt;code&gt;systemctl&lt;/code&gt; command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl status ntp
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;● ntp.service - LSB: Start NTP daemon
   Loaded: loaded (/etc/init.d/ntp; generated; vendor preset: enabled)
   Active: &lt;span class="highlight"&gt;active (running)&lt;/span&gt; since Tue 2018-09-04 15:07:03 EDT; 30min ago
     Docs: man:systemd-sysv-generator(8)
  Process: 876 ExecStart=/etc/init.d/ntp start (code=exited, status=0/SUCCESS)
    Tasks: 2 (limit: 4915)
   CGroup: /system.slice/ntp.service
           └─904 /usr/sbin/ntpd -p /var/run/ntpd.pid -g -u 105:109
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;active (running)&lt;/code&gt; status indicates that ntpd started up properly. To get more information about the status of ntpd we can use the &lt;code&gt;ntpq&lt;/code&gt; command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;ntpq -p
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;     remote           refid      st t when poll reach   delay   offset  jitter
==============================================================================
 0.debian.pool.n .POOL.          16 p    -   64    0    0.000    0.000   0.000
 1.debian.pool.n .POOL.          16 p    -   64    0    0.000    0.000   0.000
 2.debian.pool.n .POOL.          16 p    -   64    0    0.000    0.000   0.000
 3.debian.pool.n .POOL.          16 p    -   64    0    0.000    0.000   0.000
-eterna.binary.n 204.9.54.119     2 u  240  256  377   35.392    0.142   0.211
-static-96-244-9 192.168.10.254   2 u   60  256  377   10.242    1.297   2.412
+minime.fdf.net  83.157.230.212   3 u   99  256  377   24.042    0.128   0.250
*t1.time.bf1.yah 98.139.133.62    2 u   31  256  377   11.112    0.621   0.186
+x.ns.gin.ntt.ne 249.224.99.213   2 u  108  256  377    1.290   -0.073   0.132
-ord1.m-d.net    142.66.101.13    2 u  473  512  377   19.930   -1.764   0.293
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;ntpq&lt;/code&gt; is a query tool for ntpd. The &lt;code&gt;-p&lt;/code&gt; flag asks for information about the NTP servers (or &lt;strong&gt;p&lt;/strong&gt;eers) ntpd is connected to. Your output will be slightly different, but should list the default Debian pool servers plus a few others. Bear in mind that it can take a few minutes for ntpd to establish connections.&lt;/p&gt;

&lt;h2 id="switching-to-systemd-timesyncd"&gt;Switching to systemd-timesyncd&lt;/h2&gt;

&lt;p&gt;It is possible to use systemd's built-in &lt;strong&gt;timesyncd&lt;/strong&gt; component to replace ntpd. timesyncd is a lighter-weight alternative to ntpd that is more integrated with systemd. Note however that it doesn't support running as a time server, and it is slightly less sophisticated in the techniques it uses to keep your system time in sync. If you are running complex real-time distributed systems, you may want to stick with ntpd.&lt;/p&gt;

&lt;p&gt;To use timesyncd, we must first uninstall ntpd:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt purge ntp
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, start up the timesyncd service:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl start systemd-timesyncd
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, check the status of the service to make sure it's running:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl status systemd-timesyncd
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;● systemd-timesyncd.service - Network Time Synchronization
   Loaded: loaded (/lib/systemd/system/systemd-timesyncd.service; enabled; vendor preset: enabled)
  Drop-In: /lib/systemd/system/systemd-timesyncd.service.d
           └─disable-with-time-daemon.conf
   Active: &lt;span class="highlight"&gt;active (running)&lt;/span&gt; since Tue 2018-09-04 16:14:23 EDT; 1s ago
     Docs: man:systemd-timesyncd.service(8)
 Main PID: 3399 (systemd-timesyn)
   Status: "Synchronized to time server 198.60.22.240:123 (0.debian.pool.ntp.org)."
    Tasks: 2 (limit: 4915)
   CGroup: /system.slice/systemd-timesyncd.service
           └─3399 /lib/systemd/systemd-timesyncd
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can use &lt;code&gt;timedatectl&lt;/code&gt; to print out systemd's current understanding of the time:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;timedatectl
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;      Local time: Tue 2018-09-04 16:15:34 EDT
  Universal time: Tue 2018-09-04 20:15:34 UTC
        RTC time: Tue 2018-09-04 20:15:33
       Time zone: America/New_York (EDT, -0400)
&lt;span class="highlight"&gt; Network time on: yes&lt;/span&gt;
&lt;span class="highlight"&gt;NTP synchronized: yes&lt;/span&gt;
 RTC in local TZ: no
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This prints out the local time, universal time (which may be the same as local time, if you didn't switch from the UTC time zone), and some network time status information. &lt;code&gt;Network time on: yes&lt;/code&gt; means that timesyncd is enabled, and &lt;code&gt;NTP synchronized: yes&lt;/code&gt; indicates that the time has been successfully synced.&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;In this article we’ve shown how to view the system time, change time zones, work with ntpd, and switch to systemd's timesyncd service. If you have more sophisticated timekeeping needs than what we’ve covered here, you might refer to &lt;a href="https://www.eecis.udel.edu/%7Emills/ntp/html/index.html"&gt;the offical NTP documentation&lt;/a&gt;, and also take a look at &lt;a href="http://www.pool.ntp.org/"&gt;the NTP Pool Project&lt;/a&gt;, a global group of volunteers providing much of the world's NTP infrastructure.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-install-nginx-on-debian-9</id>
    <published>2018-09-04T19:26:03Z</published>
    <updated>2018-09-06T16:57:45Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-install-nginx-on-debian-9"/>
    <title>How To Install Nginx on Debian 9</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://nginx.org/"&gt;Nginx&lt;/a&gt; is one of the most popular web servers in the world and responsible for hosting some of the largest and highest-traffic sites on the internet. It is more resource-friendly than Apache in most cases and can be used as a web server or reverse proxy.&lt;/p&gt;

&lt;p&gt;In this guide, we'll discuss how to install Nginx on your Debian 9 server.&lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;Before you begin this guide, you should have a regular, non-root user with sudo privileges configured on your server and an active firewall.  You can learn how to set these up by following our &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-debian-9"&gt;initial server setup guide for Debian 9&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;When you have an account available, log in as your non-root user to begin.&lt;/p&gt;

&lt;h2 id="step-1-–-installing-nginx"&gt;Step 1 – Installing Nginx&lt;/h2&gt;

&lt;p&gt;Because Nginx is available in Debian's default repositories, it is possible to install it from these repositories using the &lt;code&gt;apt&lt;/code&gt; packaging system.&lt;/p&gt;

&lt;p&gt;Since this is our first interaction with the &lt;code&gt;apt&lt;/code&gt; packaging system in this session, let's also update our local package index so that we have access to the most recent package listings.  Afterwards, we can install &lt;code&gt;nginx&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt update
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo apt install nginx
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After accepting the procedure, &lt;code&gt;apt&lt;/code&gt; will install Nginx and any required dependencies to your server.&lt;/p&gt;

&lt;h2 id="step-2-–-adjusting-the-firewall"&gt;Step 2 – Adjusting the Firewall&lt;/h2&gt;

&lt;p&gt;Before testing Nginx, the firewall software needs to be adjusted to allow access to the service.  &lt;/p&gt;

&lt;p&gt;List the application configurations that &lt;code&gt;ufw&lt;/code&gt; knows how to work with by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw app list
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should get a listing of the application profiles:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Available applications:
...
  Nginx Full
  Nginx HTTP
  Nginx HTTPS
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, there are three profiles available for Nginx:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Nginx Full&lt;/strong&gt;: This profile opens both port &lt;code&gt;80&lt;/code&gt; (normal, unencrypted web traffic) and port &lt;code&gt;443&lt;/code&gt; (TLS/SSL encrypted traffic)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Nginx HTTP&lt;/strong&gt;: This profile opens only port &lt;code&gt;80&lt;/code&gt; (normal, unencrypted web traffic)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Nginx HTTPS&lt;/strong&gt;: This profile opens only port &lt;code&gt;443&lt;/code&gt; (TLS/SSL encrypted traffic)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is recommended that you enable the most restrictive profile that will still allow the traffic you've configured.  Since we haven't configured SSL for our server yet in this guide, we will only need to allow traffic on port &lt;code&gt;80&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can enable this by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw allow 'Nginx HTTP'
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can verify the change by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw status
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see HTTP traffic allowed in the displayed output:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Status: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere                  
Nginx HTTP                 ALLOW       Anywhere                  
OpenSSH (v6)               ALLOW       Anywhere (v6)             
Nginx HTTP (v6)            ALLOW       Anywhere (v6)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="step-3-–-checking-your-web-server"&gt;Step 3 – Checking your Web Server&lt;/h2&gt;

&lt;p&gt;At the end of the installation process, Debian 9 starts Nginx.  The web server should already be up and running.&lt;/p&gt;

&lt;p&gt;We can check with the &lt;code&gt;systemd&lt;/code&gt; init system to make sure the service is running by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;systemctl status nginx
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;● nginx.service - A high performance web server and a reverse proxy server
   Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled)
   Active: &lt;span class="highlight"&gt;active (running)&lt;/span&gt; since Tue 2018-09-04 18:15:57 UTC; 3min 28s ago
     Docs: man:nginx(8)
  Process: 2402 ExecStart=/usr/sbin/nginx -g daemon on; master_process on; (code=exited, status=0/SUCCESS)
  Process: 2399 ExecStartPre=/usr/sbin/nginx -t -q -g daemon on; master_process on; (code=exited, status=0/SUCCESS)
 Main PID: 2404 (nginx)
    Tasks: 2 (limit: 4915)
   CGroup: /system.slice/nginx.service
           ├─2404 nginx: master process /usr/sbin/nginx -g daemon on; master_process on;
           └─2405 nginx: worker process
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see above, the service appears to have started successfully.  However, the best way to test this is to actually request a page from Nginx.&lt;/p&gt;

&lt;p&gt;You can access the default Nginx landing page to confirm that the software is running properly by navigating to your server's IP address. If you do not know your server's IP address, try typing this at your server's command prompt:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;ip addr show eth0 | grep inet | awk '{ print $2; }' | sed 's/\/.*$//'
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will get back a few lines.  You can try each in your web browser to see if they work.&lt;/p&gt;

&lt;p&gt;When you have your server's IP address, enter it into your browser's address bar:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;http://&lt;span class="highlight"&gt;your_server_ip&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see the default Nginx landing page:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/nginx_1604/default_page.png" alt="Nginx default page"&gt;&lt;/p&gt;

&lt;p&gt;This page is included with Nginx to show you that the server is running correctly.&lt;/p&gt;

&lt;h2 id="step-4-–-managing-the-nginx-process"&gt;Step 4 – Managing the Nginx Process&lt;/h2&gt;

&lt;p&gt;Now that you have your web server up and running, let's review some basic management commands.&lt;/p&gt;

&lt;p&gt;To stop your web server, type:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl stop nginx
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To start the web server when it is stopped, type:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl start nginx
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To stop and then start the service again, type:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl restart nginx
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you are simply making configuration changes, Nginx can often reload without dropping connections.  To do this, type:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl reload nginx
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By default, Nginx is configured to start automatically when the server boots.  If this is not what you want, you can disable this behavior by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl disable nginx
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To re-enable the service to start up at boot, you can type:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl enable nginx
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="step-5-–-setting-up-server-blocks"&gt;Step 5 – Setting Up Server Blocks&lt;/h2&gt;

&lt;p&gt;When using the Nginx web server, &lt;em&gt;server blocks&lt;/em&gt; (similar to virtual hosts in Apache) can be used to encapsulate configuration details and host more than one domain from a single server. We will set up a domain called &lt;strong&gt;example.com&lt;/strong&gt;, but you should &lt;strong&gt;replace this with your own domain name&lt;/strong&gt;. To learn more about setting up a domain name with DigitalOcean, see our &lt;a href="https://www.digitalocean.com/docs/networking/dns/"&gt;introduction to DigitalOcean DNS&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Nginx on Debian 9 has one server block enabled by default that is configured to serve documents out of a directory at &lt;code&gt;/var/www/html&lt;/code&gt;. While this works well for a single site, it can become unwieldy if you are hosting multiple sites. Instead of modifying &lt;code&gt;/var/www/html&lt;/code&gt;, let's create a directory structure within &lt;code&gt;/var/www&lt;/code&gt; for our &lt;strong&gt;example.com&lt;/strong&gt; site, leaving &lt;code&gt;/var/www/html&lt;/code&gt; in place as the default directory to be served if a client request doesn't match any other sites.&lt;/p&gt;

&lt;p&gt;Create the directory for &lt;strong&gt;example.com&lt;/strong&gt; as follows, using the &lt;code&gt;-p&lt;/code&gt; flag to create any necessary parent directories:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo mkdir -p /var/www/&lt;span class="highlight"&gt;example.com&lt;/span&gt;/html
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, assign ownership of the directory with the &lt;code&gt;$USER&lt;/code&gt; environment variable:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo chown -R $USER:$USER /var/www/&lt;span class="highlight"&gt;example.com&lt;/span&gt;/html
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The permissions of your web roots should be correct if you haven't modified your &lt;code&gt;umask&lt;/code&gt; value, but you can make sure by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo chmod -R 755 /var/www/&lt;span class="highlight"&gt;example.com&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, create a sample &lt;code&gt;index.html&lt;/code&gt; page using &lt;code&gt;nano&lt;/code&gt; or &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-debian-9#step-six-%E2%80%94-completing-optional-configuration"&gt;your favorite editor&lt;/a&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nano /var/www/&lt;span class="highlight"&gt;example.com&lt;/span&gt;/html/index.html
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Inside, add the following sample HTML:&lt;/p&gt;
&lt;div class="code-label " title="/var/www/example.com/html/index.html"&gt;/var/www/example.com/html/index.html&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&amp;lt;html&amp;gt;
    &amp;lt;head&amp;gt;
        &amp;lt;title&amp;gt;Welcome to &lt;span class="highlight"&gt;Example.com&lt;/span&gt;!&amp;lt;/title&amp;gt;
    &amp;lt;/head&amp;gt;
    &amp;lt;body&amp;gt;
        &amp;lt;h1&amp;gt;Success!  The &lt;span class="highlight"&gt;example.com&lt;/span&gt; server block is working!&amp;lt;/h1&amp;gt;
    &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save and close the file when you are finished.&lt;/p&gt;

&lt;p&gt;In order for Nginx to serve this content, it's necessary to create a server block with the correct directives. Instead of modifying the default configuration file directly, let’s make a new one at &lt;code&gt;/etc/nginx/sites-available/&lt;span class="highlight"&gt;example.com&lt;/span&gt;&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/nginx/sites-available/&lt;span class="highlight"&gt;example.com&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Paste in the following configuration block, which is similar to the default, but updated for our new directory and domain name:&lt;/p&gt;
&lt;div class="code-label " title="/etc/nginx/sites-available/example.com"&gt;/etc/nginx/sites-available/example.com&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;server {
        listen 80;
        listen [::]:80;

        root /var/www/&lt;span class="highlight"&gt;example.com&lt;/span&gt;/html;
        index index.html index.htm index.nginx-debian.html;

        server_name &lt;span class="highlight"&gt;example.com&lt;/span&gt; www.&lt;span class="highlight"&gt;example.com&lt;/span&gt;;

        location / {
                try_files $uri $uri/ =404;
        }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notice that we’ve updated the &lt;code&gt;root&lt;/code&gt; configuration to our new directory, and the &lt;code&gt;server_name&lt;/code&gt; to our domain name.&lt;/p&gt;

&lt;p&gt;Next, let's enable the file by creating a link from it to the &lt;code&gt;sites-enabled&lt;/code&gt; directory, which Nginx reads from during startup:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ln -s /etc/nginx/sites-available/&lt;span class="highlight"&gt;example.com&lt;/span&gt; /etc/nginx/sites-enabled/
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Two server blocks are now enabled and configured to respond to requests based on their &lt;code&gt;listen&lt;/code&gt; and &lt;code&gt;server_name&lt;/code&gt; directives (you can read more about how Nginx processes these directives &lt;a href="https://www.digitalocean.com/community/tutorials/understanding-nginx-server-and-location-block-selection-algorithms"&gt;here&lt;/a&gt;):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;example.com&lt;/code&gt;: Will respond to requests for &lt;code&gt;example.com&lt;/code&gt; and &lt;code&gt;www.example.com&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;default&lt;/code&gt;: Will respond to any requests on port &lt;code&gt;80&lt;/code&gt; that do not match the other two blocks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To avoid a possible hash bucket memory problem that can arise from adding additional server names, it is necessary to adjust a single value in the &lt;code&gt;/etc/nginx/nginx.conf&lt;/code&gt; file.  Open the file:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/nginx/nginx.conf
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Find the &lt;code&gt;server_names_hash_bucket_size&lt;/code&gt; directive and remove the &lt;code&gt;#&lt;/code&gt; symbol to uncomment the line:&lt;/p&gt;
&lt;div class="code-label " title="/etc/nginx/nginx.conf"&gt;/etc/nginx/nginx.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;...
http {
    ...
    server_names_hash_bucket_size 64;
    ...
}
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save and close the file when you are finished.&lt;/p&gt;

&lt;p&gt;Next, test to make sure that there are no syntax errors in any of your Nginx files:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nginx -t
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If there aren't any problems, you will see the following output:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once your configuration test passes, restart Nginx to enable your changes:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl restart nginx
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nginx should now be serving your domain name. You can test this by navigating to &lt;code&gt;http://&lt;span class="highlight"&gt;example.com&lt;/span&gt;&lt;/code&gt;, where you should see something like this:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/nginx_server_block_1404/first_block.png" alt="Nginx first server block"&gt;&lt;/p&gt;

&lt;h2 id="step-6-–-getting-familiar-with-important-nginx-files-and-directories"&gt;Step 6 – Getting Familiar with Important Nginx Files and Directories&lt;/h2&gt;

&lt;p&gt;Now that you know how to manage the Nginx service itself, you should take a few minutes to familiarize yourself with a few important directories and files.&lt;/p&gt;

&lt;h3 id="content"&gt;Content&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/var/www/html&lt;/code&gt;: The actual web content, which by default only consists of the default Nginx page you saw earlier, is served out of the &lt;code&gt;/var/www/html&lt;/code&gt; directory.  This can be changed by altering Nginx configuration files.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id="server-configuration"&gt;Server Configuration&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/etc/nginx&lt;/code&gt;: The Nginx configuration directory.  All of the Nginx configuration files reside here.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/etc/nginx/nginx.conf&lt;/code&gt;: The main Nginx configuration file.  This can be modified to make changes to the Nginx global configuration.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/etc/nginx/sites-available/&lt;/code&gt;: The directory where per-site server blocks can be stored.  Nginx will not use the configuration files found in this directory unless they are linked to the &lt;code&gt;sites-enabled&lt;/code&gt; directory.  Typically, all server block configuration is done in this directory, and then enabled by linking to the other directory.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/etc/nginx/sites-enabled/&lt;/code&gt;: The directory where enabled per-site server blocks are stored.  Typically, these are created by linking to configuration files found in the &lt;code&gt;sites-available&lt;/code&gt; directory.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/etc/nginx/snippets&lt;/code&gt;: This directory contains configuration fragments that can be included elsewhere in the Nginx configuration.  Potentially repeatable configuration segments are good candidates for refactoring into snippets.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id="server-logs"&gt;Server Logs&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/var/log/nginx/access.log&lt;/code&gt;: Every request to your web server is recorded in this log file unless Nginx is configured to do otherwise.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/var/log/nginx/error.log&lt;/code&gt;: Any Nginx errors will be recorded in this log.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Now that you have your web server installed, you have many options for the type of content you can serve and the technologies you can use to create a richer experience for your users.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-install-mariadb-on-debian-9</id>
    <published>2018-09-04T19:32:20Z</published>
    <updated>2018-09-05T16:55:08Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-install-mariadb-on-debian-9"/>
    <title>How To Install MariaDB on Debian 9</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://mariadb.org/"&gt;MariaDB&lt;/a&gt; is an open-source database management system, commonly installed in place of MySQL as part of the popular &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-linux-apache-mariadb-php-lamp-stack-debian9"&gt;LAMP&lt;/a&gt; (Linux, Apache, MySQL, PHP/Python/Perl) stack.  It uses a relational database and SQL (Structured Query Language) to manage its data.  MariaDB was forked from MySQL in 2009 due to licensing concerns.&lt;/p&gt;

&lt;p&gt;The short version of the installation is simple: update your package index, install the &lt;code&gt;mariadb-server&lt;/code&gt; package (which points to MariaDB), and then run the included security script.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt update
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo apt install mariadb-server
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo mysql_secure_installation
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This tutorial will explain how to install MariaDB version 10.1 on a Debian 9 server.&lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;To follow this tutorial, you will need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One Debian 9 server set up by following &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-debian-9"&gt;this initial server setup guide&lt;/a&gt;, including a non-&lt;strong&gt;root&lt;/strong&gt; user with &lt;code&gt;sudo&lt;/code&gt; privileges and a firewall.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="step-1-—-installing-mariadb"&gt;Step 1 — Installing MariaDB&lt;/h2&gt;

&lt;p&gt;On Debian 9, MariaDB version 10.1 is included in the APT package repositories by default.  It is marked as the default MySQL variant by the Debian MySQL/MariaDB packaging team.&lt;/p&gt;

&lt;p&gt;To install it, update the package index on your server with &lt;code&gt;apt&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt update
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then install the package:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install mariadb-server
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will install MariaDB, but will not prompt you to set a password or make any other configuration changes.  Because this leaves your installation of MariaDB insecure, we will address this next.&lt;/p&gt;

&lt;h2 id="step-2-—-configuring-mariadb"&gt;Step 2 — Configuring MariaDB&lt;/h2&gt;

&lt;p&gt;For fresh installations, you'll want to run the included security script.  This changes some of the less secure default options for things like remote &lt;strong&gt;root&lt;/strong&gt; logins and sample users.&lt;/p&gt;

&lt;p&gt;Run the security script:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo mysql_secure_installation
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will take you through a series of prompts where you can make some changes to your MariaDB installation’s security options.  The first prompt will ask you to enter the current database &lt;strong&gt;root&lt;/strong&gt; password.  Since we have not set one up yet, press &lt;code&gt;ENTER&lt;/code&gt; to indicate "none".&lt;/p&gt;

&lt;p&gt;The next prompt asks you whether you'd like to set up a database &lt;strong&gt;root&lt;/strong&gt; password.  Type &lt;code&gt;N&lt;/code&gt; and then press &lt;code&gt;ENTER&lt;/code&gt;.  In Debian, the &lt;strong&gt;root&lt;/strong&gt; account for MariaDB is tied closely to automated system maintenance, so we should not change the configured authentication methods for that account.  Doing so would make it possible for a package update to break the database system by removing access to the administrative account.  Later, we will cover how to optionally set up an additional administrative account for password access if socket authentication is not appropriate for your use case.&lt;/p&gt;

&lt;p&gt;From there, you can press &lt;code&gt;Y&lt;/code&gt; and then &lt;code&gt;ENTER&lt;/code&gt; to accept the defaults for all the subsequent questions. This will remove some anonymous users and the test database, disable remote &lt;strong&gt;root&lt;/strong&gt; logins, and load these new rules so that MariaDB immediately respects the changes you have made.&lt;/p&gt;

&lt;h2 id="step-3-—-optional-adjusting-user-authentication-and-privileges"&gt;Step 3 — (Optional) Adjusting User Authentication and Privileges&lt;/h2&gt;

&lt;p&gt;In Debian systems running MariaDB 10.1, the &lt;strong&gt;root&lt;/strong&gt; MariaDB user is set to authenticate using the &lt;code&gt;unix_socket&lt;/code&gt; plugin by default rather than with a password. This allows for some greater security and usability in many cases, but it can also complicate things when you need to allow an external program (e.g., phpMyAdmin) administrative rights.&lt;/p&gt;

&lt;p&gt;Because the server uses the &lt;strong&gt;root&lt;/strong&gt; account for tasks like log rotation and starting and stopping the server, it is best not to change the &lt;strong&gt;root&lt;/strong&gt; account's authentication details.  Changing the account credentials in the &lt;code&gt;/etc/mysql/debian.cnf&lt;/code&gt; may work initially, but package updates could potentially overwrite those changes.  Instead of modifying the &lt;strong&gt;root&lt;/strong&gt; account, the package maintainers recommend creating a separate administrative account if you need to set up password-based access.&lt;/p&gt;

&lt;p&gt;To do so, we will be creating a new account called &lt;code&gt;admin&lt;/code&gt; with the same capabilities as the &lt;strong&gt;root&lt;/strong&gt; account, but configured for password authentication.  To do this, open up the MariaDB prompt from your terminal:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo mysql
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, we can create a new user with &lt;strong&gt;root&lt;/strong&gt; privileges and password-based access.  Change the username and password to match your preferences:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="MariaDB [(none)]&amp;gt;"&gt;GRANT ALL ON *.* TO '&lt;span class="highlight"&gt;admin&lt;/span&gt;'@'localhost' IDENTIFIED BY '&lt;span class="highlight"&gt;password&lt;/span&gt;' WITH GRANT OPTION;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Flush the privileges to ensure that they are saved and available in the current session:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="MariaDB [(none)]&amp;gt;"&gt;FLUSH PRIVILEGES;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Following this, exit the MariaDB shell:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="MariaDB [(none)]&amp;gt;"&gt;exit
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, let's test the MariaDB installation.&lt;/p&gt;

&lt;h2 id="step-4-—-testing-mariadb"&gt;Step 4 — Testing MariaDB&lt;/h2&gt;

&lt;p&gt;When installed from the default repositories, MariaDB should start running automatically. To test this, check its status.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl status mariadb
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You'll see output similar to the following:&lt;/p&gt;
&lt;div class="code-label " title="Output"&gt;Output&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;● mariadb.service - MariaDB database server
   Loaded: loaded (/lib/systemd/system/mariadb.service; enabled; vendor preset: enabled)
   Active: &lt;span class="highlight"&gt;active (running)&lt;/span&gt; since Tue 2018-09-04 16:22:47 UTC; 2h 35min ago
  Process: 15596 ExecStartPost=/bin/sh -c systemctl unset-environment _WSREP_START_POSIT
  Process: 15594 ExecStartPost=/etc/mysql/debian-start (code=exited, status=0/SUCCESS)
  Process: 15478 ExecStartPre=/bin/sh -c [ ! -e /usr/bin/galera_recovery ] &amp;amp;&amp;amp; VAR= ||   
  Process: 15474 ExecStartPre=/bin/sh -c systemctl unset-environment _WSREP_START_POSITI
  Process: 15471 ExecStartPre=/usr/bin/install -m 755 -o mysql -g root -d /var/run/mysql
 Main PID: 15567 (mysqld)
   Status: "Taking your SQL requests now..."
    Tasks: 27 (limit: 4915)
   CGroup: /system.slice/mariadb.service
           └─15567 /usr/sbin/mysqld

Sep 04 16:22:45 deb-mysql1 systemd[1]: Starting MariaDB database server...
Sep 04 16:22:46 deb-mysql1 mysqld[15567]: 2018-09-04 16:22:46 140183374869056 [Note] /usr/sbin/mysqld (mysqld 10.1.26-MariaDB-0+deb9u1) starting as process 15567 ...
Sep 04 16:22:47 deb-mysql1 systemd[1]: &lt;span class="highlight"&gt;Started MariaDB database server.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If MariaDB isn't running, you can start it with &lt;code&gt;sudo systemctl start mariadb&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For an additional check, you can try connecting to the database using the &lt;code&gt;mysqladmin&lt;/code&gt; tool, which is a client that lets you run administrative commands.  For example, this command says to connect to MariaDB as &lt;strong&gt;root&lt;/strong&gt; and return the version using the Unix socket:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo mysqladmin version
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see output similar to this:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;mysqladmin  Ver 9.1 Distrib 10.1.26-MariaDB, for debian-linux-gnu on x86_64
Copyright (c) 2000, 2017, Oracle, MariaDB Corporation Ab and others.

Server version      10.1.26-MariaDB-0+deb9u1
Protocol version    10
Connection      Localhost via UNIX socket
UNIX socket     /var/run/mysqld/mysqld.sock
Uptime:         2 hours 44 min 46 sec

Threads: 1  Questions: 36  Slow queries: 0  Opens: 21  Flush tables: 1  Open tables: 15  Queries per second avg: 0.003
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you configured a separate administrative user with password authentication, you could perform the same operation by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;mysqladmin -u &lt;span class="highlight"&gt;admin&lt;/span&gt; -p version
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This means MariaDB is up and running and that your user is able to authenticate successfully.&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;You now have a basic MariaDB setup installed on your server.  Here are a few examples of next steps you can take:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.digitalocean.com/community/tutorials/how-to-secure-mysql-and-mariadb-databases-in-a-linux-vps"&gt;Implement some additional security measures&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.digitalocean.com/community/tutorials/how-to-move-a-mysql-data-directory-to-a-new-location-on-ubuntu-16-04"&gt;Relocate the data directory&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-install-linux-apache-mariadb-php-lamp-stack-debian9</id>
    <published>2018-09-04T18:49:40Z</published>
    <updated>2018-09-04T20:28:09Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-install-linux-apache-mariadb-php-lamp-stack-debian9"/>
    <title>How To Install Linux, Apache, MariaDB, PHP (LAMP) stack on Debian 9</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;A "LAMP" stack is a group of open source software that is typically installed together to enable a server to host dynamic websites and web apps. This term is actually an acronym which represents the &lt;strong&gt;L&lt;/strong&gt;inux operating system, with the &lt;strong&gt;A&lt;/strong&gt;pache web server. The site data is stored in a &lt;strong&gt;M&lt;/strong&gt;ariaDB database, and dynamic content is processed by &lt;strong&gt;P&lt;/strong&gt;HP.&lt;/p&gt;

&lt;p&gt;In this guide, we will install a LAMP stack on a Debian 9 server.&lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;In order to complete this tutorial, you will need to have a Debian 9 server with a non-root &lt;code&gt;sudo&lt;/code&gt;-enabled user account and a basic firewall. This can be configured using our &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-debian-9"&gt;initial server setup guide for Debian 9&lt;/a&gt;. &lt;/p&gt;

&lt;h2 id="step-1-—-installing-apache-and-updating-the-firewall"&gt;Step 1 — Installing Apache and Updating the Firewall&lt;/h2&gt;

&lt;p&gt;The Apache web server is among the most popular web servers in the world. It's well-documented and has been in wide use for much of the history of the web, which makes it a great default choice for hosting a website.&lt;/p&gt;

&lt;p&gt;Install Apache using Debian's package manager, &lt;code&gt;apt&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt update
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo apt install apache2
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since this is a &lt;code&gt;sudo&lt;/code&gt; command, these operations are executed with root privileges. It will ask you for your regular user's password to verify your intentions.&lt;/p&gt;

&lt;p&gt;Once you've entered your password, &lt;code&gt;apt&lt;/code&gt; will tell you which packages it plans to install and how much extra disk space they'll take up. Press &lt;code&gt;Y&lt;/code&gt; and hit &lt;code&gt;ENTER&lt;/code&gt; to continue, and the installation will proceed.&lt;/p&gt;

&lt;p&gt;Next, assuming that you have followed the initial server setup instructions by installing and enabling the UFW firewall, make sure that your firewall allows HTTP and HTTPS traffic. &lt;/p&gt;

&lt;p&gt;When installed on Debian 9, UFW comes loaded with app profiles which you can use to tweak your firewall settings. View the full list of application profiles by running:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw app list
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;WWW&lt;/code&gt; profiles are used to manage ports used by web servers:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Available applications:
. . .
  WWW
  WWW Cache
  WWW Full
  WWW Secure
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you inspect the &lt;code&gt;WWW Full&lt;/code&gt; profile, it shows that it enables traffic to ports &lt;code&gt;80&lt;/code&gt; and &lt;code&gt;443&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw app info "WWW Full"
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Profile: WWW Full
Title: Web Server (HTTP,HTTPS)
Description: Web Server (HTTP,HTTPS)

Ports:
  &lt;span class="highlight"&gt;80&lt;/span&gt;,&lt;span class="highlight"&gt;443/tcp&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Allow incoming HTTP and HTTPS traffic for this profile:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw allow in “WWW Full”
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can do a spot check right away to verify that everything went as planned by visiting your server's public IP address in your web browser:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;http://&lt;span class="highlight"&gt;your_server_ip&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will see the default Debian 9 Apache web page, which is there for informational and testing purposes. It should look something like this:&lt;/p&gt;

&lt;p&gt;&lt;img src="http://assets.digitalocean.com/how-to-install-lamp-debian-9/small_apache_default_debian9.png" alt="Debian 9 Apache default"&gt;&lt;/p&gt;

&lt;p&gt;If you see this page, then your web server is now correctly installed and accessible through your firewall.&lt;/p&gt;

&lt;p&gt;If you do not know what your server's public IP address is, there are a number of ways you can find it. Usually, this is the address you use to connect to your server through SSH.&lt;/p&gt;

&lt;p&gt;There are a few different ways to do this from the command line. First, you could use the &lt;code&gt;iproute2&lt;/code&gt; tools to get your IP address by typing this:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;ip addr show eth0 | grep inet | awk '{ print $2; }' | sed 's/\/.*$//'
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will give you two or three lines back. They are all correct addresses, but your computer may only be able to use one of them, so feel free to try each one.&lt;/p&gt;

&lt;p&gt;An alternative method is to use the &lt;code&gt;curl&lt;/code&gt; utility to contact an outside party to tell you how &lt;em&gt;it&lt;/em&gt; sees your server. This is done by asking a specific server what your IP address is:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install curl
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;curl http://icanhazip.com
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Regardless of the method you use to get your IP address, type it into your web browser's address bar to view the default Apache page.&lt;/p&gt;

&lt;h2 id="step-2-—-installing-mariadb"&gt;Step 2 — Installing MariaDB&lt;/h2&gt;

&lt;p&gt;Now that you have your web server up and running, it is time to install MariaDB. MariaDB is a database management system. Basically, it will organize and provide access to databases where your site can store information.&lt;/p&gt;

&lt;p&gt;MariaDB is a community-built fork of MySQL. In Debian 9, the default MySQL server is MariaDB 10.1, and the &lt;code&gt;mysql-server&lt;/code&gt; package, which is normally used to install MySQL, is a transitional package that will actually install MariaDB. However, it’s recommended that you install MariaDB using the program’s actual package, &lt;code&gt;mariadb-server&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Again, use &lt;code&gt;apt&lt;/code&gt; to acquire and install this software:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install mariadb-server
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span class='note'&gt;&lt;strong&gt;Note&lt;/strong&gt;: In this case, you do not have to run &lt;code&gt;sudo apt update&lt;/code&gt; prior to the command. This is because you recently ran it in the commands above to install Apache, and the package index on your computer should already be up-to-date.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;This command, too, will show you a list of the packages that will be installed, along with the amount of disk space they'll take up. Enter &lt;code&gt;Y&lt;/code&gt; to continue.&lt;/p&gt;

&lt;p&gt;When the installation is complete, run a simple security script that comes pre-installed with MariaDB which will remove some insecure default settings and lock down access to your database system. Start the interactive script by running:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo mysql_secure_installation
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will take you through a series of prompts where you can make some changes to your MariaDB installation’s security options. The first prompt will ask you to enter the current database &lt;strong&gt;root&lt;/strong&gt; password. This is an administrative account in MariaDB that has increased privileges. Think of it as being similar to the &lt;strong&gt;root&lt;/strong&gt; account for the server itself (although the one you are configuring now is a MariaDB-specific account). Because you just installed MariaDB and haven’t made any configuration changes yet, this password will be blank, so just press &lt;code&gt;ENTER&lt;/code&gt; at the prompt.&lt;/p&gt;

&lt;p&gt;The next prompt asks you whether you'd like to set up a database &lt;strong&gt;root&lt;/strong&gt; password. Type &lt;code&gt;N&lt;/code&gt; and then press &lt;code&gt;ENTER&lt;/code&gt;. In Debian, the &lt;strong&gt;root&lt;/strong&gt; account for MariaDB is tied closely to automated system maintenance, so we should not change the configured authentication methods for that account. Doing so would make it possible for a package update to break the database system by removing access to the administrative account. Later, we will cover how to optionally set up an additional administrative account for password access if socket authentication is not appropriate for your use case.&lt;/p&gt;

&lt;p&gt;From there, you can press &lt;code&gt;Y&lt;/code&gt; and then &lt;code&gt;ENTER&lt;/code&gt; to accept the defaults for all the subsequent questions. This will remove some anonymous users and the test database, disable remote &lt;strong&gt;root&lt;/strong&gt; logins, and load these new rules so that MariaDB immediately respects the changes you have made.&lt;/p&gt;

&lt;p&gt;In new installs on Debian systems, the &lt;strong&gt;root&lt;/strong&gt; MariaDB user is set to authenticate using the &lt;code&gt;unix_socket&lt;/code&gt; plugin by default rather than with a password. This allows for some greater security and usability in many cases, but it can also complicate things when you need to allow an external program (e.g., phpMyAdmin) administrative rights.&lt;/p&gt;

&lt;p&gt;Because the server uses the &lt;strong&gt;root&lt;/strong&gt; account for tasks like log rotation and starting and stopping the server, it is best not to change the &lt;strong&gt;root&lt;/strong&gt; account's authentication details. Changing the account credentials in the &lt;code&gt;/etc/mysql/debian.cnf&lt;/code&gt; may work initially, but package updates could potentially overwrite those changes. Instead of modifying the &lt;strong&gt;root&lt;/strong&gt; account, the package maintainers recommend creating a separate administrative account if you need to set up password-based access.&lt;/p&gt;

&lt;p&gt;To do so, we will be creating a new account called &lt;code&gt;admin&lt;/code&gt; with the same capabilities as the &lt;strong&gt;root&lt;/strong&gt; account, but configured for password authentication. To do this, open up the MariaDB prompt from your terminal:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo mariadb
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, we can create a new user with &lt;strong&gt;root&lt;/strong&gt; privileges and password-based access. Change the username and password to match your preferences:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="MariaDB [(none)]&amp;gt;"&gt;GRANT ALL ON *.* TO '&lt;span class="highlight"&gt;admin&lt;/span&gt;'@'localhost' IDENTIFIED BY '&lt;span class="highlight"&gt;password&lt;/span&gt;' WITH GRANT OPTION;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Flush the privileges to ensure that they are saved and available in the current session:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="MariaDB [(none)]&amp;gt;"&gt;FLUSH PRIVILEGES;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Following this, exit the MariaDB shell:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="MariaDB [(none)]&amp;gt;"&gt;exit
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, any time you want to access your database as your new administrative user, you’ll need to authenticate as that user with the password you just set using the following command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;mariadb -u &lt;span class="highlight"&gt;admin&lt;/span&gt; -p
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At this point, your database system is set up and you can move on to installing PHP, the final component of the LAMP stack.&lt;/p&gt;

&lt;h2 id="step-3-—-installing-php"&gt;Step 3 — Installing PHP&lt;/h2&gt;

&lt;p&gt;PHP is the component of your setup that will process code to display dynamic content. It can run scripts, connect to your MariaDB databases to get information, and hand the processed content over to your web server to display.&lt;/p&gt;

&lt;p&gt;Once again, leverage the &lt;code&gt;apt&lt;/code&gt; system to install PHP. In addition, include some helper packages this time so that PHP code can run under the Apache server and talk to your MariaDB database:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install php libapache2-mod-php php-mysql
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This should install PHP without any problems. We'll test this in a moment.&lt;/p&gt;

&lt;p&gt;In most cases, you will want to modify the way that Apache serves files when a directory is requested. Currently, if a user requests a directory from the server, Apache will first look for a file called &lt;code&gt;index.html&lt;/code&gt;. We want to tell the web server to prefer PHP files over others, so make Apache look for an &lt;code&gt;index.php&lt;/code&gt; file first.&lt;/p&gt;

&lt;p&gt;To do this, type this command to open the &lt;code&gt;dir.conf&lt;/code&gt; file in a text editor with root privileges:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/apache2/mods-enabled/dir.conf
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It will look like this:&lt;/p&gt;
&lt;div class="code-label " title="/etc/apache2/mods-enabled/dir.conf"&gt;/etc/apache2/mods-enabled/dir.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&amp;lt;IfModule mod_dir.c&amp;gt;
    DirectoryIndex index.html index.cgi index.pl &lt;span class="highlight"&gt;index.php&lt;/span&gt; index.xhtml index.htm
&amp;lt;/IfModule&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Move the PHP index file (highlighted above) to the first position after the &lt;code&gt;DirectoryIndex&lt;/code&gt; specification, like this:&lt;/p&gt;
&lt;div class="code-label " title="/etc/apache2/mods-enabled/dir.conf"&gt;/etc/apache2/mods-enabled/dir.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&amp;lt;IfModule mod_dir.c&amp;gt;
    DirectoryIndex &lt;span class="highlight"&gt;index.php&lt;/span&gt; index.html index.cgi index.pl index.xhtml index.htm
&amp;lt;/IfModule&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When you are finished, save and close the file by pressing &lt;code&gt;CTRL+X&lt;/code&gt;. Confirm the save by typing &lt;code&gt;Y&lt;/code&gt; and then hit &lt;code&gt;ENTER&lt;/code&gt; to verify the file save location.&lt;/p&gt;

&lt;p&gt;After this, restart the Apache web server in order for your changes to be recognized. Do this by typing this:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl restart apache2
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can also check on the status of the &lt;code&gt;apache2&lt;/code&gt; service using &lt;code&gt;systemctl&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl status apache2
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Sample Output"&gt;Sample Output&lt;/div&gt;● apache2.service - The Apache HTTP Server
   Loaded: loaded (/lib/systemd/system/apache2.service; enabled; vendor preset: enabled)
   Active: active (running) since Tue 2018-09-04 18:23:03 UTC; 9s ago
  Process: 22209 ExecStop=/usr/sbin/apachectl stop (code=exited, status=0/SUCCESS)
  Process: 22216 ExecStart=/usr/sbin/apachectl start (code=exited, status=0/SUCCESS)
 Main PID: 22221 (apache2)
    Tasks: 6 (limit: 4915)
   CGroup: /system.slice/apache2.service
           ├─22221 /usr/sbin/apache2 -k start
           ├─22222 /usr/sbin/apache2 -k start
           ├─22223 /usr/sbin/apache2 -k start
           ├─22224 /usr/sbin/apache2 -k start
           ├─22225 /usr/sbin/apache2 -k start
           └─22226 /usr/sbin/apache2 -k start
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To enhance the functionality of PHP, you have the option to install some additional modules. To see the available options for PHP modules and libraries, pipe the results of &lt;code&gt;apt search&lt;/code&gt; into &lt;code&gt;less&lt;/code&gt;, a pager which lets you scroll through the output of other commands:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;apt search php- | less
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Use the arrow keys to scroll up and down, and press &lt;code&gt;Q&lt;/code&gt; to quit.&lt;/p&gt;

&lt;p&gt;The results are all optional components that you can install. It will give you a short description for each:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Sorting...
Full Text Search...
bandwidthd-pgsql/stable 2.0.1+cvs20090917-10 amd64
  Tracks usage of TCP/IP and builds html files with graphs

bluefish/stable 2.2.9-1+b1 amd64
  advanced Gtk+ text editor for web and software development

cacti/stable 0.8.8h+ds1-10 all
  web interface for graphing of monitoring systems

cakephp-scripts/stable 2.8.5-1 all
  rapid application development framework for PHP (scripts)

ganglia-webfrontend/stable 3.6.1-3 all
  cluster monitoring toolkit - web front-end

haserl/stable 0.9.35-2+b1 amd64
  CGI scripting program for embedded environments

kdevelop-php-docs/stable 5.0.3-1 all
  transitional package for kdevelop-php

kdevelop-php-docs-l10n/stable 5.0.3-1 all
  transitional package for kdevelop-php-l10n
…
:
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To learn more about what each module does, you could search the internet for more information about them. Alternatively, look at the long description of the package by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;apt show &lt;span class="highlight"&gt;package_name&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There will be a lot of output, with one field called &lt;code&gt;Description&lt;/code&gt; which will have a longer explanation of the functionality that the module provides.&lt;/p&gt;

&lt;p&gt;For example, to find out what the &lt;code&gt;php-cli&lt;/code&gt; module does, you could type this:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;apt show php-cli
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Along with a large amount of other information, you'll find something that looks like this:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;…
Description: command-line interpreter for the PHP scripting language (default)
 This package provides the /usr/bin/php command interpreter, useful for
 testing PHP scripts from a shell or performing general shell scripting tasks.
 .
 PHP (recursive acronym for PHP: Hypertext Preprocessor) is a widely-used
 open source general-purpose scripting language that is especially suited
 for web development and can be embedded into HTML.
 .
 This package is a dependency package, which depends on Debian's default
 PHP version (currently 7.0).
…
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If, after researching, you decide you would like to install a package, you can do so by using the &lt;code&gt;apt install&lt;/code&gt; command like you have been doing for the other software.&lt;/p&gt;

&lt;p&gt;If you decided that &lt;code&gt;php-cli&lt;/code&gt; is something that you need, you could type:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install php-cli
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you want to install more than one module, you can do that by listing each one, separated by a space, following the &lt;code&gt;apt install&lt;/code&gt; command, like this:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install &lt;span class="highlight"&gt;package1&lt;/span&gt; &lt;span class="highlight"&gt;package2&lt;/span&gt; &lt;span class="highlight"&gt;...&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At this point, your LAMP stack is installed and configured. Before making any more changes or deploying an application, though, it would be helpful to proactively test out your PHP configuration in case there are any issues that should be addressed.&lt;/p&gt;

&lt;h2 id="step-4-—-testing-php-processing-on-your-web-server"&gt;Step 4 — Testing PHP Processing on your Web Server&lt;/h2&gt;

&lt;p&gt;In order to test that your system is configured properly for PHP, create a very basic PHP script called &lt;code&gt;info.php&lt;/code&gt;. In order for Apache to find this file and serve it correctly, it must be saved to a very specific directory called the &lt;em&gt;web root&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;In Debian 9, this directory is located at &lt;code&gt;/var/www/html/&lt;/code&gt;. Create the file at that location by running:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /var/www/html/info.php
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will open a blank file. Add the following text, which is valid PHP code, inside the file:&lt;/p&gt;
&lt;div class="code-label " title="/var/www/html/info.php"&gt;/var/www/html/info.php&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&amp;lt;?php
phpinfo();
?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When you are finished, save and close the file.&lt;/p&gt;

&lt;p&gt;Now you can test whether your web server is able to correctly display content generated by this PHP script. To try this out, visit this page in your web browser. You'll need your server's public IP address again.&lt;/p&gt;

&lt;p&gt;The address you will want to visit is:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;http://&lt;span class="highlight"&gt;your_server_ip&lt;/span&gt;/info.php
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The page that you come to should look something like this:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/how-to-install-lamp-debian-9/small_php_info_debian9.png" alt="Debian 9 default PHP info"&gt;&lt;/p&gt;

&lt;p&gt;This page provides some basic information about your server from the perspective of PHP. It is useful for debugging and to ensure that your settings are being applied correctly.&lt;/p&gt;

&lt;p&gt;If you can see this page in your browser, then your PHP is working as expected.&lt;/p&gt;

&lt;p&gt;You probably want to remove this file after this test because it could actually give information about your server to unauthorized users. To do this, run the following command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo rm /var/www/html/info.php
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can always recreate this page if you need to access the information again later.&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Now that you have a LAMP stack installed, you have many choices for what to do next. Basically, you've installed a platform that will allow you to install most kinds of websites and web software on your server.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-reset-your-mysql-or-mariadb-root-password-on-ubuntu-18-04</id>
    <published>2018-08-31T20:23:48Z</published>
    <updated>2018-09-04T18:18:34Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-reset-your-mysql-or-mariadb-root-password-on-ubuntu-18-04"/>
    <title>How To Reset Your MySQL or MariaDB Root Password on Ubuntu 18.04</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;Forgetting passwords happens to the best of us. If you forget or lose the &lt;strong&gt;root&lt;/strong&gt; password to your MySQL or MariaDB database, you can still gain access and reset the password if you have access to the server and a user account with &lt;code&gt;sudo&lt;/code&gt; privileges.&lt;/p&gt;

&lt;p&gt;&lt;span class='note'&gt;&lt;strong&gt;Note:&lt;/strong&gt; On fresh Ubuntu 18.04 installations, the default MySQL or MariaDB configuration usually allows you to access the database (with full administrative privileges) without providing a password as long as you make the connection from the system's &lt;strong&gt;root&lt;/strong&gt; account. In this scenario it may not be necessary to reset the password. Before you proceed with resetting your database &lt;strong&gt;root&lt;/strong&gt; password, try to access the database with the &lt;code&gt;sudo mysql&lt;/code&gt; command. If this results in an &lt;em&gt;access denied&lt;/em&gt; error, follow the steps in this tutorial.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;This tutorial demonstrates how to reset the &lt;strong&gt;root&lt;/strong&gt; password for MySQL and MariaDB databases installed with the &lt;code&gt;apt&lt;/code&gt; package manager on Ubuntu 18.04.  The procedure for changing the root password differs depending on whether you have MySQL or MariaDB installed, as well as the default systemd configuration that ships with the distribution or packages from other vendors. While the instructions in this tutorial may work with other system or database server versions, they have been tested specifically with Ubuntu 18.04 and distribution-supplied packages.&lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;To recover your MySQL or MariaDB &lt;strong&gt;root&lt;/strong&gt; password, you will need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Access to the Ubuntu 18.04 server running MySQL or MariaDB with a sudo user or other way of accessing the server with root privileges. In order to try out the recovery methods in this tutorial without affecting your production server, use the &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-18-04"&gt;initial server setup tutorial&lt;/a&gt; to create a test server with a regular, non-root user with sudo privileges. Then install MySQL following &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-mysql-on-ubuntu-18-04"&gt;How to install MySQL on Ubuntu 18.04&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="step-1-—-identifying-the-database-version-and-stopping-the-server"&gt;Step 1 — Identifying the Database Version and Stopping the Server&lt;/h2&gt;

&lt;p&gt;Ubuntu 18.04 runs either MySQL or MariaDB, a popular drop-in replacement which is fully compatible with MySQL. You'll need to use different commands to recover the &lt;code&gt;root&lt;/code&gt; password depending on which of these you have installed, so follow the steps in this section to determine which database server you're running.&lt;/p&gt;

&lt;p&gt;Check your version with the following command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;mysql --version
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you're running MariaDB, you'll see "MariaDB" preceded by the version number in the output:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="MariaDB output"&gt;MariaDB output&lt;/div&gt;mysql  Ver 15.1 Distrib &lt;span class="highlight"&gt;10.1.29&lt;/span&gt;-&lt;span class="highlight"&gt;MariaDB&lt;/span&gt;, for debian-linux-gnu (x86_64) using readline 5.2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You'll see output like this if you're running MySQL:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="MySQL output"&gt;MySQL output&lt;/div&gt;mysql  Ver 14.14 Distrib &lt;span class="highlight"&gt;5.7.22&lt;/span&gt;, for Linux (x86_64) using  EditLine wrapper
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Make note of which database, as this determines the appropriate commands to follow in the rest of this tutorial. &lt;/p&gt;

&lt;p&gt;In order to change the &lt;strong&gt;root&lt;/strong&gt; password, you'll need to shut down the database server. If you're running MariaDB, you can do so with the following command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl stop &lt;span class="highlight"&gt;mariadb&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For MySQL, shut down the database server by running:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl stop &lt;span class="highlight"&gt;mysql&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With the database stopped, you can restart it in safe mode to reset the root password.&lt;/p&gt;

&lt;h2 id="step-2-—-restarting-the-database-server-without-permission-checks"&gt;Step 2 — Restarting the Database Server Without Permission Checks&lt;/h2&gt;

&lt;p&gt;Running MySQL and MariaDB without permission checking allows accessing the database command line with root privileges without providing a valid password. To do this, you need to stop the database from loading the &lt;em&gt;grant tables&lt;/em&gt;, which store user privilege information. Since this is a bit of a security risk, you may also want to disable networking to prevent other clients from connecting to the temporarily vulnerable server.&lt;/p&gt;

&lt;p&gt;Depending on which database server you've installed, the way of starting the server without loading the &lt;em&gt;grant tables&lt;/em&gt; differs.&lt;/p&gt;

&lt;h3 id="configuring-mariadb-to-start-without-grant-tables"&gt;Configuring MariaDB to Start Without Grant Tables&lt;/h3&gt;

&lt;p&gt;In order to start the MariaDB server without the grant tables, we'll use the &lt;code&gt;systemd&lt;/code&gt; unit file  to set additional parameters for the MariaDB server daemon. &lt;/p&gt;

&lt;p&gt;Execute the following command which sets the MYSQLD_OPTS environment variable used by MariaDB upon startup. The &lt;code&gt;--skip-grant-tables&lt;/code&gt; and &lt;code&gt;--skip-networking&lt;/code&gt; options tell MariaDB to start up without loading the grant tables or networking features:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl set-environment MYSQLD_OPTS="&lt;span class="highlight"&gt;--skip-grant-tables --skip-networking&lt;/span&gt;"
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then start the MariaDB server:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl start mariadb
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This command won't produce any output, but it will restart the database server, taking into account the new environment variable settings.&lt;/p&gt;

&lt;p&gt;You can ensure it started with &lt;code&gt;sudo systemctl status mariadb&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Now you should be able to connect to the database as the MariaDB &lt;strong&gt;root&lt;/strong&gt; user without supplying a password:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo mysql -u root
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You'll immediately see a database shell prompt:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="MariaDB prompt"&gt;MariaDB prompt&lt;/div&gt;Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that you have access to the database server, you can change the &lt;strong&gt;root&lt;/strong&gt; password as shown in Step 3.&lt;/p&gt;

&lt;h3 id="configuring-mysql-to-start-without-grant-tables"&gt;Configuring MySQL to Start Without Grant Tables&lt;/h3&gt;

&lt;p&gt;In order to start the MySQL server without its grant tables, you'll alter the systemd configuration for MySQL to pass additional command-line parameters to the server upon startup. &lt;/p&gt;

&lt;p&gt;To do this, execute the following command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl edit mysql
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This command will open a new file in the &lt;code&gt;nano&lt;/code&gt; editor, which you'll use to edit MySQL's &lt;em&gt;service overrides&lt;/em&gt;. These change the default service parameters for MySQL. This file will be empty, so add the following content:&lt;/p&gt;
&lt;div class="code-label " title="MySQL service overrides"&gt;MySQL service overrides&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;[Service]
ExecStart=
ExecStart=/usr/sbin/mysqld --daemonize --pid-file=/run/mysqld/mysqld.pid &lt;span class="highlight"&gt;--skip-grant-tables --skip-networking&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first &lt;code&gt;ExecStart&lt;/code&gt; statement clears the default value, while the second one provides &lt;code&gt;systemd&lt;/code&gt; with the new startup command including parameters to disable loading the grant tables and networking capabilities.&lt;/p&gt;

&lt;p&gt;Press &lt;code&gt;CTRL-x&lt;/code&gt; to exit the file, then &lt;code&gt;Y&lt;/code&gt; to save the changes that you made, then &lt;code&gt;ENTER&lt;/code&gt; to confirm the file name. &lt;/p&gt;

&lt;p&gt;Reload the &lt;code&gt;systemd&lt;/code&gt; configuration to apply these changes:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl daemon-reload
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now start the MySQL server: &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl start mysql
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The command will show no output, but the database server will start. The grant tables and networking will not be enabled.&lt;/p&gt;

&lt;p&gt;Connect to the database as the root user:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo mysql -u root
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You'll immediately see a database shell prompt:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="MySQL prompt"&gt;MySQL prompt&lt;/div&gt;Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that you have access to the server, you can change the &lt;strong&gt;root&lt;/strong&gt; password.&lt;/p&gt;

&lt;h2 id="step-3-—-changing-the-root-password"&gt;Step 3 — Changing the Root Password&lt;/h2&gt;

&lt;p&gt;The database server is now running in a limited mode; the grant tables are not loaded and there's no networking support enabled. This lets you access the server without providing a password, but it prohibits you from executing commands that alter data. To reset the &lt;strong&gt;root&lt;/strong&gt; password, you must load the grant tables now that you've gained access to the server.&lt;/p&gt;

&lt;p&gt;Tell the database server to reload the grant tables by issuing the &lt;code&gt;FLUSH PRIVILEGES&lt;/code&gt; command.&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;FLUSH PRIVILEGES;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can now change the &lt;strong&gt;root&lt;/strong&gt; password. The method you use depends on whether you are using MariaDB or MySQL.&lt;/p&gt;

&lt;h3 id="changing-the-mariadb-password"&gt;Changing the MariaDB Password&lt;/h3&gt;

&lt;p&gt;If you are using MariaDB, execute the following statement to set the password for the &lt;strong&gt;root&lt;/strong&gt; account, making sure to replace &lt;code&gt;new_password&lt;/code&gt; with a strong new password that you'll remember.&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="MariaDB [(none)]&amp;gt;"&gt;UPDATE mysql.user SET password = PASSWORD('&lt;span class="highlight"&gt;new_password&lt;/span&gt;') WHERE user = '&lt;span class="highlight"&gt;root&lt;/span&gt;';
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You'll see this output indicating that the password changed:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;MariaDB allows using custom authentication mechanisms, so execute the following two statements to make sure MariaDB will use its default authentication mechanism for the new password you assigned to the &lt;strong&gt;root&lt;/strong&gt; account:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="MariaDB [(none)]&amp;gt;"&gt;UPDATE mysql.user SET authentication_string = '' WHERE user = '&lt;span class="highlight"&gt;root&lt;/span&gt;';
&lt;/li&gt;&lt;li class="line" prefix="MariaDB [(none)]&amp;gt;"&gt;UPDATE mysql.user SET plugin = '' WHERE user = '&lt;span class="highlight"&gt;root&lt;/span&gt;';
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You'll see the following output for each statement:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Query OK, 0 rows affected (0.01 sec)
Rows matched: 1  Changed: 0  Warnings: 0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The password is now changed. Type &lt;code&gt;exit&lt;/code&gt; to exit the MariaDB console and proceed to Step 4 to restart the database server in normal mode.&lt;/p&gt;

&lt;h3 id="changing-the-mysql-password"&gt;Changing the MySQL Password&lt;/h3&gt;

&lt;p&gt;For MySQL, execute the following statement to change the &lt;strong&gt;root&lt;/strong&gt; user's password, replacing &lt;code&gt;new_password&lt;/code&gt; with a strong password you'll remember:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;UPDATE mysql.user SET authentication_string = PASSWORD('&lt;span class="highlight"&gt;new_password&lt;/span&gt;') WHERE user = '&lt;span class="highlight"&gt;root&lt;/span&gt;';
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You'll see this output indicating the password was changed successfully:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;MySQL allows using custom authentication mechanisms, so execute the following statement to tell MySQL touse its default authentication mechanism to authenticate the &lt;strong&gt;root&lt;/strong&gt; user using the new password: &lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;UPDATE mysql.user SET plugin = 'mysql_native_password' WHERE user = '&lt;span class="highlight"&gt;root&lt;/span&gt;';
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You'll see output similar to the previous command:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The password is now changed. Exit the MySQL console by typing &lt;code&gt;exit&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Let's restart the database in normal operational mode.&lt;/p&gt;

&lt;h2 id="step-4-—-reverting-your-database-server-to-normal-settings"&gt;Step 4 — Reverting Your Database Server to Normal Settings&lt;/h2&gt;

&lt;p&gt;In order to restart the database server in its normal mode, you have to revert the changes you made so that networking is enabled and the grant tables are loaded. Again, the method you use depends on whether you used MariaDB or MySQL.&lt;/p&gt;

&lt;p&gt;For MariaDB, unset the &lt;code&gt;MYSQLD_OPTS&lt;/code&gt; environment variable you set previously:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl unset-environment MYSQLD_OPTS
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, restart the service using &lt;code&gt;systemctl&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl restart mariadb
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For MySQL, remove the modified systemd configuration:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl revert mysql
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You'll see output similar to the following:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Removed /etc/systemd/system/mysql.service.d/override.conf.
Removed /etc/systemd/system/mysql.service.d.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, reload the systemd configuration to apply the changes:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl daemon-reload
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, restart the service:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl restart mysql
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The database is now restarted and is back to its normal state. Confirm that the new password works by logging in as the &lt;strong&gt;root&lt;/strong&gt; user with a password:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;mysql -u root &lt;span class="highlight"&gt;-p&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You'll be prompted for a password. Enter your new password and you'll gain access to the database prompt as expected.&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;You have restored administrative access to the MySQL or MariaDB server. Make sure the new password you chose is strong and secure and keep it in a safe place.&lt;/p&gt;

&lt;p&gt;For more information on user management, authentication mechanisms, or ways of resetting database password for other version of MySQL or MariaDB, please refer to the official &lt;a href="https://dev.mysql.com/doc/"&gt;MySQL documentation&lt;/a&gt; or &lt;a href="https://mariadb.com/kb/en/library/documentation/"&gt;MariaDB documentation&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-install-node-js-on-debian-9</id>
    <published>2018-09-04T17:08:58Z</published>
    <updated>2018-09-06T16:58:36Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-install-node-js-on-debian-9"/>
    <title>How To Install Node.js on Debian 9</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://nodejs.org/en/"&gt;Node.js&lt;/a&gt; is a JavaScript platform for general-purpose programming that allows users to build network applications quickly.  By leveraging JavaScript on both the front and backend, Node.js makes development more consistent and integrated.&lt;/p&gt;

&lt;p&gt;In this guide, we'll show you how to get started with Node.js on a Debian 9 server.&lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;This guide assumes that you are using Debian 9.  Before you begin, you should have a non-root user account with sudo privileges set up on your system. You can learn how to set this up by following the &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-debian-9"&gt;initial server setup for Debian 9&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id="installing-the-distro-stable-version-for-debian"&gt;Installing the Distro-Stable Version for Debian&lt;/h2&gt;

&lt;p&gt;Debian contains a version of Node.js in its default repositories. At the time of writing, this version is 4.8.2, which will reach end-of-life at the end of April 2018. If you would like to experiment with the language using a stable and sufficient option, then installing from the repositories may make sense. It is recommended, however, that for development and production use cases you install a more recent version with a PPA. We will discuss how to install from a PPA in the next step. &lt;/p&gt;

&lt;p&gt;To get the distro-stable version of Node.js, you can use the &lt;code&gt;apt&lt;/code&gt; package manager. First, refresh your local package index: &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt update
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then install the Node.js package from the repositories:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install nodejs
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If the package in the repositories suits your needs, then this is all you need to do to get set up with Node.js.  &lt;/p&gt;

&lt;p&gt;To check which version of Node.js you have installed after these initial steps, type:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nodejs -v
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Because of a conflict with another package, the executable from the Debian repositories is called &lt;code&gt;nodejs&lt;/code&gt; instead of &lt;code&gt;node&lt;/code&gt;.  Keep this in mind as you are running software.&lt;/p&gt;

&lt;p&gt;Once you have established which version of Node.js you have installed from the Debian repositories, you can decide whether or not you would like to work with different versions, package archives, or version managers. Next, we'll discuss these elements, along with more flexible and robust methods of installation.&lt;/p&gt;

&lt;h2 id="installing-using-a-ppa"&gt;Installing Using a PPA&lt;/h2&gt;

&lt;p&gt;To work with a more recent version of Node.js, you can add the &lt;em&gt;PPA&lt;/em&gt; (personal package archive) maintained by NodeSource.  This will have more up-to-date versions of Node.js than the official Debian repositories, and will allow you to choose between Node.js v4.x (the older long-term support version, which will be supported until the end of April 2018), Node.js v6.x (supported until April of 2019), Node.js v8.x (the current LTS version, supported until December of 2019), and Node.js v10.x (the latest version, supported until April of 2021).&lt;/p&gt;

&lt;p&gt;Let's first update the local package index and install &lt;code&gt;curl&lt;/code&gt;, which you will use to access the PPA:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt update
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo apt install curl
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, let's install the PPA in order to get access to its contents.  From your home directory, use &lt;code&gt;curl&lt;/code&gt; to retrieve the installation script for your preferred version, making sure to replace &lt;code&gt;&lt;span class="highlight"&gt;10.x&lt;/span&gt;&lt;/code&gt; with your preferred version string (if different):&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cd ~
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;curl -sL https://deb.nodesource.com/setup_&lt;span class="highlight"&gt;10.x&lt;/span&gt; -o nodesource_setup.sh
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can inspect the contents of this script with &lt;code&gt;nano&lt;/code&gt; or &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-debian-9#step-six-%E2%80%94-completing-optional-configuration"&gt;your preferred text editor&lt;/a&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nano nodesource_setup.sh
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Run the script under &lt;code&gt;sudo&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo bash nodesource_setup.sh
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The PPA will be added to your configuration and your local package cache will be updated automatically. After running the setup script, you can install the Node.js package in the same way you did above:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install nodejs
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To check which version of Node.js you have installed after these initial steps, type:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nodejs -v
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;v&lt;span class="highlight"&gt;10.9.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;nodejs&lt;/code&gt; package contains the &lt;code&gt;nodejs&lt;/code&gt; binary as well as &lt;code&gt;npm&lt;/code&gt;, so you don't need to install &lt;code&gt;npm&lt;/code&gt; separately. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm&lt;/code&gt; uses a configuration file in your home directory to keep track of updates. It will be created the first time you run &lt;code&gt;npm&lt;/code&gt;. Execute this command to verify that &lt;code&gt;npm&lt;/code&gt; is installed and to create the configuration file:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;npm -v
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;&lt;span class="highlight"&gt;6.2.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In order for some &lt;code&gt;npm&lt;/code&gt; packages to work (those that require compiling code from source, for example), you will need to install the &lt;code&gt;build-essential&lt;/code&gt; package:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install build-essential
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You now have the necessary tools to work with &lt;code&gt;npm&lt;/code&gt; packages that require compiling code from source.&lt;/p&gt;

&lt;h2 id="installing-using-nvm"&gt;Installing Using NVM&lt;/h2&gt;

&lt;p&gt;An alternative to installing Node.js through &lt;code&gt;apt&lt;/code&gt; is to use a tool called &lt;code&gt;nvm&lt;/code&gt;, which stands for "Node.js Version Manager".  Rather than working at the operating system level, &lt;code&gt;nvm&lt;/code&gt; works at the level of an independent directory within your home directory. This means that you can install multiple self-contained versions of Node.js without affecting the entire system. &lt;/p&gt;

&lt;p&gt;Controlling your environment with &lt;code&gt;nvm&lt;/code&gt; allows you to access the newest versions of Node.js and retain and manage previous releases. It is a different utility from &lt;code&gt;apt&lt;/code&gt;, however, and the versions of Node.js that you manage with it are distinct from those you manage with &lt;code&gt;apt&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;To download the &lt;code&gt;nvm&lt;/code&gt; installation script from the &lt;a href="https://github.com/creationix/nvm"&gt;project's GitHub page&lt;/a&gt;, you can use &lt;code&gt;curl&lt;/code&gt;. Note that the version number may differ from what is highlighted here: &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;curl -sL https://raw.githubusercontent.com/creationix/nvm/&lt;span class="highlight"&gt;v0.33.11&lt;/span&gt;/install.sh -o install_nvm.sh
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Inspect the installation script with &lt;code&gt;nano&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nano install_nvm.sh
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Run the script with &lt;code&gt;bash&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;bash install_nvm.sh
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It will install the software into a subdirectory of your home directory at &lt;code&gt;~/.nvm&lt;/code&gt;.  It will also add the necessary lines to your &lt;code&gt;~/.profile&lt;/code&gt; file to use the file.&lt;/p&gt;

&lt;p&gt;To gain access to the &lt;code&gt;nvm&lt;/code&gt; functionality, you'll need to either log out and log back in again or source the &lt;code&gt;~/.profile&lt;/code&gt; file so that your current session knows about the changes:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;source ~/.profile
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With &lt;code&gt;nvm&lt;/code&gt; installed, you can install isolated Node.js versions. For information about the versions of Node.js that are available, type:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nvm ls-remote
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;...
         v8.11.1   (Latest LTS: Carbon)
         v9.0.0
         v9.1.0
         v9.2.0
         v9.2.1
         v9.3.0
         v9.4.0
         v9.5.0
         v9.6.0
         v9.6.1
         v9.7.0
         v9.7.1
         v9.8.0
         v9.9.0
        v9.10.0
        v9.10.1
        v9.11.0
        v9.11.1
        v10.0.0  
        v10.1.0
        v10.2.0
        v10.2.1
        v10.3.0
        v10.4.0
        v10.4.1
        v10.5.0
        v10.6.0
        v10.7.0
        v10.8.0
        v10.9.0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, the current LTS version at the time of this writing is v8.11.1.  You can install that by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nvm install &lt;span class="highlight"&gt;8.11.1&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Usually, &lt;code&gt;nvm&lt;/code&gt; will switch to use the most recently installed version.  You can tell &lt;code&gt;nvm&lt;/code&gt; to use the version you just downloaded by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nvm use &lt;span class="highlight"&gt;8.11.1&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When you install Node.js using &lt;code&gt;nvm&lt;/code&gt;, the executable is called &lt;code&gt;node&lt;/code&gt;.  You can see the version currently being used by the shell by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;node -v
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;v&lt;span class="highlight"&gt;8.11.1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you have multiple Node.js versions, you can see what is installed by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nvm ls
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you wish to default one of the versions, type:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nvm alias default &lt;span class="highlight"&gt;8.11.1&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This version will be automatically selected when a new session spawns.  You can also reference it by the alias like this:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nvm use default
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Each version of Node.js will keep track of its own packages and has &lt;code&gt;npm&lt;/code&gt; available to manage these.&lt;/p&gt;

&lt;p&gt;You can also have &lt;code&gt;npm&lt;/code&gt; install packages to the Node.js project's &lt;code&gt;./node_modules&lt;/code&gt; directory. Use the following syntax to install the &lt;code&gt;express&lt;/code&gt; module:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;npm install &lt;span class="highlight"&gt;express&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you'd like to install the module globally, making it available to other projects using the same version of Node.js, you can add the &lt;code&gt;-g&lt;/code&gt; flag:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;npm install -g &lt;span class="highlight"&gt;express&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will install the package in:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;~/.nvm/versions/node/&lt;span class="highlight"&gt;node_version&lt;/span&gt;/lib/node_modules/&lt;span class="highlight"&gt;express&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Installing the module globally will let you run commands from the command line, but you'll have to link the package into your local sphere to require it from within a program:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;npm link &lt;span class="highlight"&gt;express&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can learn more about the options available to you with nvm by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nvm help
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="removing-node-js"&gt;Removing Node.js&lt;/h2&gt;

&lt;p&gt;You can uninstall Node.js using &lt;code&gt;apt&lt;/code&gt; or &lt;code&gt;nvm&lt;/code&gt;, depending on the version you want to target. To remove versions installed from the repositories or from the PPA, you will need to work with the &lt;code&gt;apt&lt;/code&gt; utility at the system level.&lt;/p&gt;

&lt;p&gt;To remove either of these versions, type the following:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt remove nodejs
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This command will remove the package and the configuration files. &lt;/p&gt;

&lt;p&gt;To uninstall a version of Node.js that you have enabled using &lt;code&gt;nvm&lt;/code&gt;, first determine whether or not the version you would like to remove is the current active version:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nvm current
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If the version you are targeting is &lt;strong&gt;not&lt;/strong&gt; the current active version, you can run:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nvm uninstall &lt;span class="highlight"&gt;node_version&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This command will uninstall the selected version of Node.js.&lt;/p&gt;

&lt;p&gt;If the version you would like to remove &lt;strong&gt;is&lt;/strong&gt; the current active version, you must first deactivate &lt;code&gt;nvm&lt;/code&gt; to enable your changes:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nvm deactivate
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can now uninstall the current version using the &lt;code&gt;uninstall&lt;/code&gt; command above, which will remove all files associated with the targeted version of Node.js except the cached files that can be used for reinstallation. &lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;There are a quite a few ways to get up and running with Node.js on your Debian 9 server.  Your circumstances will dictate which of the above methods is best for your needs.  While using the packaged version in the Debian repository is an option for experimentation, installing from a PPA and working with &lt;code&gt;npm&lt;/code&gt; or &lt;code&gt;nvm&lt;/code&gt; offers additional flexibility.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/initial-server-setup-with-debian-9</id>
    <published>2018-08-31T19:46:06Z</published>
    <updated>2018-09-04T14:15:48Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-debian-9"/>
    <title>Initial Server Setup with Debian 9</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;When you first create a new Debian 9 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 and will give you a solid foundation for subsequent actions.&lt;/p&gt;

&lt;h2 id="step-one-—-logging-in-as-root"&gt;Step One — Logging in as Root&lt;/h2&gt;

&lt;p&gt;To log into your server, you will need to know your &lt;strong&gt;server's public IP address&lt;/strong&gt;.  You will also need the password or, if you installed an SSH key for authentication, the private key for the &lt;strong&gt;root&lt;/strong&gt; user's account.  If you have not already logged into your server, you may want to follow our guide on &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-connect-to-your-droplet-with-ssh"&gt;how to connect to your Droplet with SSH&lt;/a&gt;, which covers this process in detail.&lt;/p&gt;

&lt;p&gt;If you are not already connected to your server, go ahead and log in as the &lt;strong&gt;root&lt;/strong&gt; user using the following command (substitute the highlighted portion of the command with your server's public IP address):&lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;ssh root@&lt;span class="highlight"&gt;your_server_ip&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Accept the warning about host authenticity if it appears.  If you are using password authentication, provide your &lt;strong&gt;root&lt;/strong&gt; password to log in.  If you are using an SSH key that is passphrase protected, you may be prompted to enter the passphrase the first time you use the key each session.  If this is your first time logging into the server with a password, you may also be prompted to change the &lt;strong&gt;root&lt;/strong&gt; password.&lt;/p&gt;

&lt;h3 id="about-root"&gt;About Root&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;root&lt;/strong&gt; user is the administrative user in a Linux environment that has very broad privileges.  Because of the heightened privileges of the &lt;strong&gt;root&lt;/strong&gt; account, you are &lt;em&gt;discouraged&lt;/em&gt; from using it on a regular basis.  This is because part of the power inherent with the &lt;strong&gt;root&lt;/strong&gt; account is the ability to make very destructive changes, even by accident.&lt;/p&gt;

&lt;p&gt;The next step is to set up an alternative user account with a reduced scope of influence for day-to-day work.  We'll teach you how to gain increased privileges during the times when you need them.&lt;/p&gt;

&lt;h2 id="step-two-—-creating-a-new-user"&gt;Step Two — Creating a New User&lt;/h2&gt;

&lt;p&gt;Once you are logged in as &lt;strong&gt;root&lt;/strong&gt;, we're prepared to add the new user account that we will use to log in from now on.&lt;/p&gt;

&lt;span class='note'&gt;&lt;p&gt;
&lt;strong&gt;Note&lt;/strong&gt;: In some environments, a package called &lt;code&gt;unscd&lt;/code&gt; may be installed by default in order to speed up requests to name servers like LDAP.  The most recent version currently available in Debian contains &lt;a href="https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=844447"&gt;a bug&lt;/a&gt; that causes certain commands (like the &lt;code&gt;adduser&lt;/code&gt; command below) to produce additional output that looks like this:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;sent invalidate(passwd) request, exiting
sent invalidate(group) request, exiting
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These messages are harmless, but if you wish to avoid them, it is safe to remove the &lt;code&gt;unscd&lt;/code&gt; package if you do not not plan on using systems like LDAP for user information:&lt;/p&gt;
&lt;pre class="code-pre super_user"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="#"&gt;apt remove unscd
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;/p&gt;&lt;/span&gt;

&lt;p&gt;This example creates a new user called &lt;strong&gt;sammy&lt;/strong&gt;, but you should replace it with a username that you like:&lt;/p&gt;
&lt;pre class="code-pre super_user"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="#"&gt;adduser &lt;span class="highlight"&gt;sammy&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will be asked a few questions, starting with the account password.&lt;/p&gt;

&lt;p&gt;Enter a strong password and, optionally, fill in any of the additional information if you would like.  This is not required and you can just hit &lt;code&gt;ENTER&lt;/code&gt; in any field you wish to skip.&lt;/p&gt;

&lt;h2 id="step-three-—-granting-administrative-privileges"&gt;Step Three — Granting Administrative Privileges&lt;/h2&gt;

&lt;p&gt;Now, we have a new user account with regular account privileges.  However, we may sometimes need to do administrative tasks.&lt;/p&gt;

&lt;p&gt;To avoid having to log out of our normal user and log back in as the &lt;strong&gt;root&lt;/strong&gt; account, we can set up what is known as "superuser" or &lt;strong&gt;root&lt;/strong&gt; privileges for our normal account.  This will allow our normal user to run commands with administrative privileges by putting the word &lt;code&gt;sudo&lt;/code&gt; before each command. &lt;/p&gt;

&lt;p&gt;To add these privileges to our new user, we need to add the new user to the &lt;strong&gt;sudo&lt;/strong&gt; group. By default, on Debian 9, users who belong to the &lt;strong&gt;sudo&lt;/strong&gt; group are allowed to use the &lt;code&gt;sudo&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;As &lt;strong&gt;root&lt;/strong&gt;, run this command to add your new user to the &lt;strong&gt;sudo&lt;/strong&gt; group (substitute the highlighted word with your new user):&lt;/p&gt;
&lt;pre class="code-pre super_user"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="#"&gt;usermod -aG sudo &lt;span class="highlight"&gt;sammy&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, when logged in as your regular user, you can type &lt;code&gt;sudo&lt;/code&gt; before commands to perform actions with superuser privileges.&lt;/p&gt;

&lt;h2 id="step-four-—-setting-up-a-basic-firewall"&gt;Step Four — Setting Up a Basic Firewall&lt;/h2&gt;

&lt;p&gt;Debian servers can use firewalls to make sure only connections to certain services are allowed.  Although the &lt;code&gt;iptables&lt;/code&gt; firewall is installed by default, Debian does not strongly recommend any specific firewall.  In this guide, we will install and use the UFW firewall to help set policies and manage exceptions.&lt;/p&gt;

&lt;p&gt;We can use the &lt;code&gt;apt&lt;/code&gt; package manager to install UFW.  Update the local index to retrieve the latest information about available packages and then install the firewall by typing:&lt;/p&gt;
&lt;pre class="code-pre super_user"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="#"&gt;apt update
&lt;/li&gt;&lt;li class="line" prefix="#"&gt;apt install ufw
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span class='note'&gt;&lt;strong&gt;Note:&lt;/strong&gt; If your servers are running on DigitalOcean, you can optionally use &lt;a href="https://www.digitalocean.com/community/tutorials/an-introduction-to-digitalocean-cloud-firewalls"&gt;DigitalOcean Cloud Firewalls&lt;/a&gt; instead of the UFW firewall.  We recommend using only one firewall at a time to avoid conflicting rules that may be difficult to debug.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;Firewall profiles allow UFW to manage sets of firewall rules for applications by name.  Profiles for some common software are bundled with UFW by default and packages can register additional profiles with UFW during the installation process.  OpenSSH, the service allowing us to connect to our server now, has a firewall profile that we can use.&lt;/p&gt;

&lt;p&gt;You can see this by typing:&lt;/p&gt;
&lt;pre class="code-pre super_user"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="#"&gt;ufw app list
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Available applications:
  . . .
  OpenSSH
  . . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We need to make sure that the firewall allows SSH connections so that we can log back in next time.  We can allow these connections by typing:&lt;/p&gt;
&lt;pre class="code-pre super_user"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="#"&gt;ufw allow OpenSSH
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Afterwards, we can enable the firewall by typing:&lt;/p&gt;
&lt;pre class="code-pre super_user"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="#"&gt;ufw enable
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Type "&lt;code&gt;y&lt;/code&gt;" and press &lt;code&gt;ENTER&lt;/code&gt; to proceed.  You can see that SSH connections are still allowed by typing:&lt;/p&gt;
&lt;pre class="code-pre super_user"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="#"&gt;ufw status
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Status: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere
OpenSSH (v6)               ALLOW       Anywhere (v6)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As &lt;strong&gt;the firewall is currently blocking all connections except for SSH&lt;/strong&gt;, if you install and configure additional services, you will need to adjust the firewall settings to allow acceptable traffic in.  You can learn some common UFW operations in &lt;a href="https://www.digitalocean.com/community/tutorials/ufw-essentials-common-firewall-rules-and-commands"&gt;this guide&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id="step-five-—-enabling-external-access-for-your-regular-user"&gt;Step Five — Enabling External Access for Your Regular User&lt;/h2&gt;

&lt;p&gt;Now that we have a regular user for daily use, we need to make sure we can SSH into the account directly.&lt;/p&gt;

&lt;p&gt;&lt;span class='note'&gt;&lt;strong&gt;Note:&lt;/strong&gt; Until verifying that you can log in and use &lt;code&gt;sudo&lt;/code&gt; with your new user, we recommend staying logged in as &lt;strong&gt;root&lt;/strong&gt;.  This way, if you have problems, you can troubleshoot and make any necessary changes as &lt;strong&gt;root&lt;/strong&gt;.  If you are using a DigitalOcean Droplet and experience problems with your &lt;strong&gt;root&lt;/strong&gt; SSH connection, you can &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-use-the-digitalocean-console-to-access-your-droplet"&gt;log into the Droplet using the DigitalOcean Console&lt;/a&gt;.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;The process for configuring SSH access for your new user depends on whether your server's &lt;strong&gt;root&lt;/strong&gt; account uses a password or SSH keys for authentication.&lt;/p&gt;

&lt;h3 id="if-the-root-account-uses-password-authentication"&gt;If the Root Account Uses Password Authentication&lt;/h3&gt;

&lt;p&gt;If you logged in to your &lt;strong&gt;root&lt;/strong&gt; account &lt;em&gt;using a password&lt;/em&gt;, then password authentication is enabled for SSH.  You can SSH to your new user account by opening up a new terminal session and using SSH with your new username:&lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;ssh &lt;span class="highlight"&gt;sammy&lt;/span&gt;@&lt;span class="highlight"&gt;your_server_ip&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After entering your regular user's password, you will be logged in.  Remember, if you need to run a command with administrative privileges, type &lt;code&gt;sudo&lt;/code&gt; before it like this:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo &lt;span class="highlight"&gt;command_to_run&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will be prompted for your regular user password when using &lt;code&gt;sudo&lt;/code&gt; for the first time each session (and periodically afterwards).&lt;/p&gt;

&lt;p&gt;To enhance your server's security, &lt;strong&gt;we strongly recommend setting up SSH keys instead of using password authentication&lt;/strong&gt;.  Follow our guide on &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-set-up-ssh-keys-on-debian-9"&gt;setting up SSH keys on Debian 9&lt;/a&gt; to learn how to configure key-based authentication.&lt;/p&gt;

&lt;h3 id="if-the-root-account-uses-ssh-key-authentication"&gt;If the Root Account Uses SSH Key Authentication&lt;/h3&gt;

&lt;p&gt;If you logged in to your &lt;strong&gt;root&lt;/strong&gt; account &lt;em&gt;using SSH keys&lt;/em&gt;, then password authentication is &lt;em&gt;disabled&lt;/em&gt; for SSH.  You will need to add a copy of your local public key to the new user's &lt;code&gt;~/.ssh/authorized_keys&lt;/code&gt; file to log in successfully.&lt;/p&gt;

&lt;p&gt;Since your public key is already in the &lt;strong&gt;root&lt;/strong&gt; account's &lt;code&gt;~/.ssh/authorized_keys&lt;/code&gt; file on the server, we can copy that file and directory structure to our new user account in our existing session with the &lt;code&gt;cp&lt;/code&gt; command.  Afterwards, we can adjust ownership of the files using the &lt;code&gt;chown&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;Make sure to change the highlighted portions of the command below to match your regular user's name:&lt;/p&gt;
&lt;pre class="code-pre super_user"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="#"&gt;cp -r ~/.ssh /home/&lt;span class="highlight"&gt;sammy&lt;/span&gt;
&lt;/li&gt;&lt;li class="line" prefix="#"&gt;chown -R &lt;span class="highlight"&gt;sammy&lt;/span&gt;:&lt;span class="highlight"&gt;sammy&lt;/span&gt; /home/&lt;span class="highlight"&gt;sammy&lt;/span&gt;/.ssh
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, open up a new terminal session and using SSH with your new username:&lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;ssh &lt;span class="highlight"&gt;sammy&lt;/span&gt;@&lt;span class="highlight"&gt;your_server_ip&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should be logged in to the new user account without using a password.  Remember, if you need to run a command with administrative privileges, type &lt;code&gt;sudo&lt;/code&gt; before it like this:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo &lt;span class="highlight"&gt;command_to_run&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will be prompted for your regular user password when using &lt;code&gt;sudo&lt;/code&gt; for the first time each session (and periodically afterwards).&lt;/p&gt;

&lt;h2 id="step-six-—-completing-optional-configuration"&gt;Step Six — Completing Optional Configuration&lt;/h2&gt;

&lt;p&gt;Now that we have a strong baseline configuration, we can consider a few optional steps to make the system more accessible.  The following sections cover a few additional tweaks focused on usability.&lt;/p&gt;

&lt;h3 id="installing-man-pages"&gt;Installing man Pages&lt;/h3&gt;

&lt;p&gt;Debian provides extensive manuals for most software in the form of &lt;code&gt;man&lt;/code&gt; pages.  However, the &lt;code&gt;man&lt;/code&gt; command is not always included by default on minimal installations.&lt;/p&gt;

&lt;p&gt;Install the &lt;code&gt;man-db&lt;/code&gt; package to install the &lt;code&gt;man&lt;/code&gt; command and the manual databases:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install man-db
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, to view the manual for a component, you can type:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;man &lt;span class="highlight"&gt;command&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For example, to view the manual for the &lt;code&gt;top&lt;/code&gt; command, type:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;man top
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Most packages in the Debian repositories include manual pages as part of their installation.&lt;/p&gt;

&lt;h3 id="changing-the-default-editor"&gt;Changing the Default Editor&lt;/h3&gt;

&lt;p&gt;Debian offers a wide variety of text editors, some of which are included in the base system.  Commands with integrated editor support, like &lt;code&gt;visudo&lt;/code&gt; and &lt;code&gt;systemctl edit&lt;/code&gt;, pass text to the &lt;code&gt;editor&lt;/code&gt; command, which is mapped to the system default editor.  Setting the default editor according to your preferences can help you configure your system more easily and avoid frustration.&lt;/p&gt;

&lt;p&gt;If your preferred editor is not installed by default, use &lt;code&gt;apt&lt;/code&gt; to install it first:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install &lt;span class="highlight"&gt;your_preferred_editor&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, you can view the current default and modify the selection using the &lt;code&gt;update-alternatives&lt;/code&gt; command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo update-alternatives --config editor
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The command displays a table of the editors it knows about with a prompt to change the default:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;There are 8 choices for the alternative editor (providing /usr/bin/editor).

  Selection    Path                Priority   Status
------------------------------------------------------------
* 0            /usr/bin/joe         70        auto mode
  1            /bin/nano            40        manual mode
  2            /usr/bin/jmacs       50        manual mode
  3            /usr/bin/joe         70        manual mode
  4            /usr/bin/jpico       50        manual mode
  5            /usr/bin/jstar       50        manual mode
  6            /usr/bin/rjoe        25        manual mode
  7            /usr/bin/vim.basic   30        manual mode
  8            /usr/bin/vim.tiny    15        manual mode

Press &amp;lt;enter&amp;gt; to keep the current choice[*], or type selection number:
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The asterisk in the far left column indicates the current selection.  To change the default, type the "Selection" number for your preferred editor and press &lt;code&gt;Enter&lt;/code&gt;.  For example, to use &lt;code&gt;nano&lt;/code&gt; as the default editor given the above table, we would choose &lt;code&gt;1&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Press &amp;lt;enter&amp;gt; to keep the current choice[*], or type selection number: &lt;span class="highlight"&gt;1&lt;/span&gt;
update-alternatives: using /bin/nano to provide /usr/bin/editor (editor) in manual mode
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;From now on, your preferred editor will be used by commands like &lt;code&gt;visudo&lt;/code&gt; and &lt;code&gt;systemctl edit&lt;/code&gt;, or when the &lt;code&gt;editor&lt;/code&gt; command is called.&lt;/p&gt;

&lt;h2 id="where-to-go-from-here"&gt;Where To Go From Here?&lt;/h2&gt;

&lt;p&gt;At this point, you have a solid foundation for your server. You can install any of the software you need on your server now.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-set-up-logical-replication-with-postgresql-10-on-ubuntu-18-04</id>
    <published>2018-08-22T22:02:39Z</published>
    <updated>2018-08-31T21:02:48Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-set-up-logical-replication-with-postgresql-10-on-ubuntu-18-04"/>
    <title>How To Set Up Logical Replication with PostgreSQL 10 on Ubuntu 18.04</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;When setting up an application for production, it's often useful to have multiple copies of your database in place. The process of keeping database copies in sync is called &lt;em&gt;replication&lt;/em&gt;. Replication can provide high-availability horizontal scaling for high volumes of simultaneous read operations, along with reduced read latencies. It also allows for peer-to-peer replication between geographically distributed database servers. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.postgresql.org/"&gt;PostgreSQL&lt;/a&gt; is an open-source object-relational database system that is highly extensible and compliant with &lt;a href="https://en.wikipedia.org/wiki/ACID_(computer_science)"&gt;&lt;em&gt;ACID&lt;/em&gt;&lt;/a&gt; (Atomicity, Consistency, Isolation, Durability) and the SQL standard. Version 10.0 of PostgreSQL introduced support for &lt;em&gt;logical replication&lt;/em&gt;, in addition to &lt;em&gt;physical replication&lt;/em&gt;. In a logical replication scheme, high-level write operations are streamed from a &lt;em&gt;master&lt;/em&gt; database server into one or more &lt;em&gt;replica&lt;/em&gt; database servers. In a physical replication scheme, binary write operations are instead streamed from master to replica, producing a byte-for-byte exact copy of the original content. In cases where you would like to target a particular subset of data, such as off-load reporting, patching, or upgrading, logical replication can offer speed and flexibility. &lt;/p&gt;

&lt;p&gt;In this tutorial, you will configure logical replication with PostgreSQL 10 on two Ubuntu 18.04 servers, with one server acting as the master and the other as the replica. By the end of the tutorial you will be able to replicate data from the master server to the replica using logical replication. &lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;To follow this tutorial, you will need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Two Ubuntu 18.04 servers, which we'll name &lt;strong&gt;db-master&lt;/strong&gt; and &lt;strong&gt;db-replica&lt;/strong&gt;, each set up with a regular user account and sudo privileges. To set these up, follow &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-16-04"&gt;this initial server setup tutorial&lt;/a&gt;. &lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.digitalocean.com/docs/networking/private-networking/quickstart/"&gt;Private networking enabled&lt;/a&gt; on your servers. Private networking allows for communication between your servers without the security risks associated with exposing databases to the public internet.&lt;/li&gt;
&lt;li&gt;PostgreSQL 10 installed on both servers, following Step 1 of &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-and-use-postgresql-on-ubuntu-18-04"&gt;How To Install and Use PostgreSQL on Ubuntu 18.04&lt;/a&gt;. &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="step-1-—-configuring-postgresql-for-logical-replication"&gt;Step 1 — Configuring PostgreSQL for Logical Replication&lt;/h2&gt;

&lt;p&gt;There are several configuration settings you will need to modify to enable logical replication between your servers. First, you'll configure Postgres to listen on the private network interface instead of the public one, as exposing data over the public network is a security risk. Then you'll configure the appropriate settings to allow replication to &lt;strong&gt;db-replica&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;On &lt;strong&gt;db-master&lt;/strong&gt;, open &lt;code&gt;/etc/postgresql/10/main/postgresql.conf&lt;/code&gt;, the main server configuration file:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/postgresql/10/main/postgresql.conf
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Find the following line:&lt;/p&gt;
&lt;div class="code-label " title="/etc/postgresql/10/main/postgresql.conf"&gt;/etc/postgresql/10/main/postgresql.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;...
#listen_addresses = 'localhost'         # what IP address(es) to listen on;
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Uncomment it by removing the &lt;code&gt;#&lt;/code&gt;, and add your &lt;code&gt;&lt;span class="highlight"&gt;db_master_private_ip_address&lt;/span&gt;&lt;/code&gt; to enable connections on the private network:&lt;/p&gt;

&lt;p&gt;&lt;span class='note'&gt;&lt;strong&gt;Note:&lt;/strong&gt; In this step and the steps that follow, make sure to use the &lt;strong&gt;private&lt;/strong&gt; IP addresses of your servers, and not their public IPs. Exposing a database server to the public internet is a considerable security risk. &lt;br&gt;&lt;/span&gt;&lt;/p&gt;
&lt;div class="code-label " title="/etc/postgresql/10/main/postgresql.conf"&gt;/etc/postgresql/10/main/postgresql.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;...
listen_addresses = 'localhost, &lt;span class="highlight"&gt;db_master_private_ip_address&lt;/span&gt;'
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This makes &lt;strong&gt;db-master&lt;/strong&gt; listen for incoming connections on the private network in addition to the loopback interface.&lt;/p&gt;

&lt;p&gt;Next, find the following line:&lt;/p&gt;
&lt;div class="code-label " title="/etc/postgresql/10/main/postgresql.conf"&gt;/etc/postgresql/10/main/postgresql.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;...
#wal_level = replica                    # minimal, replica, or logical
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Uncomment it, and change it to set the PostgreSQL &lt;a href="https://www.postgresql.org/docs/current/static/wal-intro.html"&gt;&lt;em&gt;Write Ahead Log&lt;/em&gt;&lt;/a&gt; (WAL) level to &lt;code&gt;logical&lt;/code&gt;. This increases the volume of entries in the log, adding the necessary information for extracting discrepancies or changes to particular data sets:&lt;/p&gt;
&lt;div class="code-label " title="/etc/postgresql/10/main/postgresql.conf"&gt;/etc/postgresql/10/main/postgresql.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;...
wal_level = &lt;span class="highlight"&gt;logical&lt;/span&gt;
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The entries on this log will be consumed by the replica server, allowing for the replication of the high-level write operations from the master.&lt;/p&gt;

&lt;p&gt;Save the file and close it.&lt;/p&gt;

&lt;p&gt;Next, let's edit &lt;code&gt;/etc/postgresql/10/main/pg_hba.conf&lt;/code&gt;, the file that controls allowed hosts, authentication, and access to databases:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/postgresql/10/main/pg_hba.conf
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After the last line, let's add a line to allow incoming network connections from &lt;strong&gt;db-replica&lt;/strong&gt;. We'll use &lt;strong&gt;db-replica&lt;/strong&gt;'s private IP address, and specify that connections are allowed from all users and databases:&lt;/p&gt;
&lt;div class="code-label " title="/etc/postgresql/10/main/pg_hba.conf"&gt;/etc/postgresql/10/main/pg_hba.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;...
# TYPE      DATABASE        USER            ADDRESS                               METHOD
...
host         all            all             &lt;span class="highlight"&gt;db_replica_private_ip_address&lt;/span&gt;/32      md5
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Incoming network connections will now be allowed from &lt;strong&gt;db-replica&lt;/strong&gt;, authenticated by a password hash &lt;a href="https://en.wikipedia.org/wiki/MD5"&gt;(md5)&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Save the file and close it.&lt;/p&gt;

&lt;p&gt;Next, let's set our firewall rules to allow traffic from &lt;strong&gt;db-replica&lt;/strong&gt; to port &lt;code&gt;5432&lt;/code&gt; on &lt;strong&gt;db-master&lt;/strong&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw allow from &lt;span class="highlight"&gt;db_replica_private_ip_address&lt;/span&gt; to any port 5432
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, restart the PostgreSQL server for the changes to take effect:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl restart postgresql 
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With your configuration set to allow logical replication, you can now move on to creating a database, user role, and table.&lt;/p&gt;

&lt;h2 id="step-2-—-setting-up-a-database-user-role-and-table"&gt;Step 2 — Setting Up a Database, User Role, and Table&lt;/h2&gt;

&lt;p&gt;To test the functionality of your replication settings, let's create a database, table, and user role. You will create an &lt;code&gt;&lt;span class="highlight"&gt;example&lt;/span&gt;&lt;/code&gt; database with a sample table, which you can then use to test logical replication between your servers. You will also create a dedicated user and assign them privileges over both the database and the table. &lt;/p&gt;

&lt;p&gt;First, open the &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-and-use-postgresql-on-ubuntu-18-04#using-postgresql-roles-and-databases"&gt;&lt;code&gt;psql&lt;/code&gt; prompt&lt;/a&gt; as the &lt;strong&gt;postgres&lt;/strong&gt; user with the following command on both &lt;strong&gt;db-master&lt;/strong&gt; and &lt;strong&gt;db-replica&lt;/strong&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo -u postgres psql
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre command second-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo -u postgres psql
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Create a new database called &lt;code&gt;&lt;span class="highlight"&gt;example&lt;/span&gt;&lt;/code&gt; on both hosts:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="postgres=#"&gt;CREATE DATABASE &lt;span class="highlight"&gt;example&lt;/span&gt;;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre custom_prefix second-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="postgres=#"&gt;CREATE DATABASE &lt;span class="highlight"&gt;example&lt;/span&gt;;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span class='note'&gt;&lt;strong&gt;Note:&lt;/strong&gt; The final &lt;code&gt;;&lt;/code&gt; in these commands is required. On interactive sessions, PostgreSQL will not execute SQL commands until you terminate them with a semicolon. Meta-commands (those starting with a backslash, like &lt;code&gt;\q&lt;/code&gt; and &lt;code&gt;\c&lt;/code&gt;) directly control the psql client itself, and are therefore exempt from this rule. For more on meta-commands and the psql client, please refer to the &lt;a href="https://www.postgresql.org/docs/current/static/app-psql.html"&gt;PostgreSQL documentation&lt;/a&gt;.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;Using the &lt;code&gt;\connect&lt;/code&gt; meta-command, connect to the databases you just created on each host:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="postgres=#"&gt;\c &lt;span class="highlight"&gt;example&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre custom_prefix second-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="postgres=#"&gt;\c &lt;span class="highlight"&gt;example&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Create a new table called &lt;code&gt;&lt;span class="highlight"&gt;widgets&lt;/span&gt;&lt;/code&gt; with arbitrary fields on both hosts:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="example=#"&gt;CREATE TABLE &lt;span class="highlight"&gt;widgets&lt;/span&gt;
&lt;/li&gt;&lt;li class="line" prefix="example=#"&gt;(
&lt;/li&gt;&lt;li class="line" prefix="example=#"&gt;    id SERIAL,
&lt;/li&gt;&lt;li class="line" prefix="example=#"&gt;    name TEXT,
&lt;/li&gt;&lt;li class="line" prefix="example=#"&gt;    price DECIMAL,
&lt;/li&gt;&lt;li class="line" prefix="example=#"&gt;    CONSTRAINT widgets_pkey PRIMARY KEY (id)
&lt;/li&gt;&lt;li class="line" prefix="example=#"&gt;);
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre custom_prefix second-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="example=#"&gt;CREATE TABLE &lt;span class="highlight"&gt;widgets&lt;/span&gt;
&lt;/li&gt;&lt;li class="line" prefix="example=#"&gt;(
&lt;/li&gt;&lt;li class="line" prefix="example=#"&gt;    id SERIAL,
&lt;/li&gt;&lt;li class="line" prefix="example=#"&gt;    name TEXT,
&lt;/li&gt;&lt;li class="line" prefix="example=#"&gt;    price DECIMAL,
&lt;/li&gt;&lt;li class="line" prefix="example=#"&gt;    CONSTRAINT widgets_pkey PRIMARY KEY (id)
&lt;/li&gt;&lt;li class="line" prefix="example=#"&gt;);
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The table on &lt;strong&gt;db-replica&lt;/strong&gt; does not need to be identical to its &lt;strong&gt;db-master&lt;/strong&gt; counterpart. However, it must contain every single column present on the table at &lt;strong&gt;db-master&lt;/strong&gt;. Additional columns must not have &lt;code&gt;NOT NULL&lt;/code&gt; or other constraints. If they do, replication will fail. &lt;/p&gt;

&lt;p&gt;On &lt;strong&gt;db-master&lt;/strong&gt;, let's create a new user role with the &lt;code&gt;REPLICATION&lt;/code&gt; option and a login password. The &lt;code&gt;REPLICATION&lt;/code&gt; attribute must be assigned to any role used for replication. We will call our user &lt;code&gt;&lt;span class="highlight"&gt;sammy&lt;/span&gt;&lt;/code&gt;, but you can replace this with your own username. Make sure to also replace &lt;code&gt;&lt;span class="highlight"&gt;my_password&lt;/span&gt;&lt;/code&gt; with your own secure password:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="example=#"&gt;CREATE ROLE &lt;span class="highlight"&gt;sammy&lt;/span&gt; WITH REPLICATION LOGIN PASSWORD '&lt;span class="highlight"&gt;my_password&lt;/span&gt;';
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Make a note of your password, as you will use it later on &lt;strong&gt;db-replica&lt;/strong&gt; to set up replication.&lt;/p&gt;

&lt;p&gt;Still on &lt;strong&gt;db-master&lt;/strong&gt;, grant full privileges on the &lt;code&gt;&lt;span class="highlight"&gt;example&lt;/span&gt;&lt;/code&gt; database to the user role you just created: &lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="example=#"&gt;GRANT ALL PRIVILEGES ON DATABASE &lt;span class="highlight"&gt;example&lt;/span&gt; TO &lt;span class="highlight"&gt;sammy&lt;/span&gt;;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, grant privileges on all of the tables contained in the database to your user:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="example=#"&gt;GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO &lt;span class="highlight"&gt;sammy&lt;/span&gt;;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;a href="https://www.postgresql.org/docs/current/static/ddl-schemas.html"&gt;&lt;code&gt;public&lt;/code&gt; schema&lt;/a&gt; is a default schema in each database into which tables are automatically placed. &lt;/p&gt;

&lt;p&gt;With these privileges set, you can now move on to making the tables in your &lt;code&gt;&lt;span class="highlight"&gt;example&lt;/span&gt;&lt;/code&gt; database available for replication.&lt;/p&gt;

&lt;h2 id="step-3-—-setting-up-a-publication"&gt;Step 3 — Setting Up a Publication&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Publications&lt;/em&gt; are the mechanism that PostgreSQL uses to make tables available for replication. The database server will keep track internally of the connection and replication status of any replica servers associated with a given publication. On &lt;strong&gt;db-master&lt;/strong&gt;, you will create a publication, &lt;code&gt;&lt;span class="highlight"&gt;my_publication&lt;/span&gt;&lt;/code&gt;, that will function as a master copy of the data that will be sent to your &lt;em&gt;subscribers&lt;/em&gt; — in our case, &lt;strong&gt;db-replica&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;On &lt;strong&gt;db-master&lt;/strong&gt;, create a publication called &lt;code&gt;&lt;span class="highlight"&gt;my_publication&lt;/span&gt;&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="example=#"&gt;CREATE PUBLICATION &lt;span class="highlight"&gt;my_publication&lt;/span&gt;;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add the &lt;code&gt;&lt;span class="highlight"&gt;widgets&lt;/span&gt;&lt;/code&gt; table you created previously to it:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="example=#"&gt;ALTER PUBLICATION &lt;span class="highlight"&gt;my_publication&lt;/span&gt; ADD TABLE &lt;span class="highlight"&gt;widgets&lt;/span&gt;;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With your publication in place, you can now add a subscriber that will pull data from it. &lt;/p&gt;

&lt;h2 id="step-4-—-creating-a-subscription"&gt;Step 4 — Creating a Subscription&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Subscriptions&lt;/em&gt; are used by PostgreSQL to connect to existing publications. A publication can have many subscriptions across different replica servers, and replica servers can also have their own publications with subscribers. To access the data from the table you created on &lt;strong&gt;db-master&lt;/strong&gt;, you will need to create a subscription to the publication you created in the previous step, &lt;code&gt;&lt;span class="highlight"&gt;my_publication&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;On &lt;strong&gt;db-replica&lt;/strong&gt;, let's create a subscription called &lt;code&gt;&lt;span class="highlight"&gt;my_subscription&lt;/span&gt;&lt;/code&gt;. The &lt;code&gt;CREATE SUBSCRIPTION&lt;/code&gt; command will name the subscription, while the &lt;code&gt;CONNECTION&lt;/code&gt; parameter will define the connection string to the publisher. This string will include the master server's connection details and login credentials, including the username and password you defined earlier, along with the name of the &lt;code&gt;&lt;span class="highlight"&gt;example&lt;/span&gt;&lt;/code&gt; database. Once again, remember to use &lt;strong&gt;db-master&lt;/strong&gt;'s private IP address, and replace &lt;code&gt;&lt;span class="highlight"&gt;my_password&lt;/span&gt;&lt;/code&gt; with your own password:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix second-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="example=#"&gt;CREATE SUBSCRIPTION &lt;span class="highlight"&gt;my_subscription&lt;/span&gt; CONNECTION 'host=&lt;span class="highlight"&gt;db_master_private_ip_address&lt;/span&gt; port=5432 password=&lt;span class="highlight"&gt;my_password&lt;/span&gt; user=&lt;span class="highlight"&gt;sammy&lt;/span&gt; dbname=&lt;span class="highlight"&gt;example&lt;/span&gt;' PUBLICATION &lt;span class="highlight"&gt;my_publication&lt;/span&gt;;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will see the following output confirming the subscription:&lt;/p&gt;
&lt;pre class="code-pre  second-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;NOTICE:  created replication slot "my_subscription" on publisher
CREATE SUBSCRIPTION
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Upon creating a subscription, PostgreSQL will automatically sync any pre-existing data from the master to the replica. In our case there is no data to sync since the &lt;code&gt;&lt;span class="highlight"&gt;widgets&lt;/span&gt;&lt;/code&gt; table is empty, but this is a useful feature when adding new subscriptions to an existing database.&lt;/p&gt;

&lt;p&gt;With a subscription in place, let's test the setup by adding some demo data to the &lt;code&gt;&lt;span class="highlight"&gt;widgets&lt;/span&gt;&lt;/code&gt; table.&lt;/p&gt;

&lt;h2 id="step-5-—-testing-and-troubleshooting"&gt;Step 5 — Testing and Troubleshooting&lt;/h2&gt;

&lt;p&gt;To test replication between our master and replica, let's add some data to the &lt;code&gt;&lt;span class="highlight"&gt;widgets&lt;/span&gt;&lt;/code&gt; table and verify that it replicates correctly. &lt;/p&gt;

&lt;p&gt;On &lt;strong&gt;db-master&lt;/strong&gt;, insert the following data on the &lt;code&gt;&lt;span class="highlight"&gt;widgets&lt;/span&gt;&lt;/code&gt; table:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="example=#"&gt;INSERT INTO &lt;span class="highlight"&gt;widgets&lt;/span&gt; (name, price) VALUES ('Hammer', 4.50), ('Coffee Mug', 6.20), ('Cupholder', 3.80);
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On &lt;strong&gt;db-replica&lt;/strong&gt;, run the following query to fetch all the entries on this table:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix second-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="example=#"&gt;SELECT * FROM &lt;span class="highlight"&gt;widgets&lt;/span&gt;;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should now see:&lt;/p&gt;
&lt;pre class="code-pre  second-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt; id |    name    | price 
----+------------+-------
  1 | Hammer     |  4.50
  2 | Coffee Mug |  6.20
  3 | Cupholder  |  3.80
(3 rows)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Success! The entries have been successfully replicated from &lt;strong&gt;db-master&lt;/strong&gt; to &lt;strong&gt;db-replica&lt;/strong&gt;. From now on, all &lt;code&gt;INSERT&lt;/code&gt;, &lt;code&gt;UPDATE&lt;/code&gt;, and &lt;code&gt;DELETE&lt;/code&gt; queries will be replicated across servers unidirectionally.&lt;/p&gt;

&lt;p&gt;One thing to note about write queries on replica servers is that they are not replicated back to the master server. PostgreSQL currently has limited support for resolving conflicts when the data between servers diverges. If there is a conflict, the replication will stop and PostgreSQL will wait until the issue is manually fixed by the database administrator. For that reason, most applications will direct all write operations to the master server, and distribute reads among available replica servers.&lt;/p&gt;

&lt;p&gt;You can now exit the &lt;code&gt;psql&lt;/code&gt; prompt on both servers:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="postgres=#"&gt;\q
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre custom_prefix second-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="postgres=#"&gt;\q
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that you have finished testing your setup, you can add and replicate data on your own. &lt;/p&gt;

&lt;h2 id="troubleshooting"&gt;Troubleshooting&lt;/h2&gt;

&lt;p&gt;If replication doesn't seem to be working, a good first step is checking the PostgreSQL log on &lt;strong&gt;db-replica&lt;/strong&gt; for any possible errors:&lt;/p&gt;
&lt;pre class="code-pre command second-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;tail /var/log/postgresql/postgresql-10-main.log
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here are some common problems that can prevent replication from working:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Private networking is not enabled on both servers, or the servers are on different networks;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;db-master&lt;/strong&gt; is not configured to listen for connections on the correct private network IP;&lt;/li&gt;
&lt;li&gt;The Write Ahead Log level on &lt;strong&gt;db-master&lt;/strong&gt; is incorrectly configured (it must be set to &lt;code&gt;logical&lt;/code&gt;);&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;db-master&lt;/strong&gt; is not configured to accept incoming connections from the correct &lt;strong&gt;db-replica&lt;/strong&gt; private IP address;&lt;/li&gt;
&lt;li&gt;A firewall like UFW is blocking incoming PostgreSQL connections on port &lt;code&gt;5432&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;There are mismatched table names or fields between &lt;strong&gt;db-master&lt;/strong&gt; and &lt;strong&gt;db-replica&lt;/strong&gt;;&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;&lt;span class="highlight"&gt;sammy&lt;/span&gt;&lt;/code&gt; database role is missing the required permissions to access the &lt;code&gt;&lt;span class="highlight"&gt;example&lt;/span&gt;&lt;/code&gt; database on &lt;strong&gt;db-master&lt;/strong&gt;;&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;&lt;span class="highlight"&gt;sammy&lt;/span&gt;&lt;/code&gt; database role is missing the &lt;code&gt;REPLICATION&lt;/code&gt; option on &lt;strong&gt;db-master&lt;/strong&gt;;&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;&lt;span class="highlight"&gt;sammy&lt;/span&gt;&lt;/code&gt; database role is missing the required permissions to access the &lt;code&gt;&lt;span class="highlight"&gt;widgets&lt;/span&gt;&lt;/code&gt; table on &lt;strong&gt;db-master&lt;/strong&gt;;&lt;/li&gt;
&lt;li&gt;The table wasn't added to the publication on &lt;strong&gt;db-master&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After resolving the existing problem(s), replication should take place automatically. If it doesn't, use following command to remove the existing subscription before recreating it:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix second-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="example=#"&gt;DROP SUBSCRIPTION &lt;span class="highlight"&gt;my_subscription&lt;/span&gt;;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;In this tutorial you've successfully installed PostgreSQL 10 on two Ubuntu 18.04 servers and configured logical replication between them. &lt;/p&gt;

&lt;p&gt;You now have the required knowledge to experiment with horizontal read scaling, high availability, and the geographical distribution of your PostgreSQL database by adding additional replica servers.&lt;/p&gt;

&lt;p&gt;To learn more about logical replication in PostgreSQL 10, you can read the &lt;a href="https://www.postgresql.org/docs/10/static/logical-replication.html"&gt;chapter on the topic&lt;/a&gt; on the official PostgreSQL documentation, as well as the manual entries on the &lt;a href="https://www.postgresql.org/docs/10/static/sql-createpublication.html"&gt;&lt;code&gt;CREATE PUBLICATION&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://www.postgresql.org/docs/10/static/sql-createsubscription.html"&gt;&lt;code&gt;CREATE SUBSCRIPTION&lt;/code&gt;&lt;/a&gt; commands.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-set-up-ssh-keys-on-debian-9</id>
    <published>2018-08-31T17:42:17Z</published>
    <updated>2018-08-31T17:52:45Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-set-up-ssh-keys-on-debian-9"/>
    <title>How to Set Up SSH Keys on Debian 9</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;SSH, or secure shell, is an encrypted protocol used to administer and communicate with servers. When working with a Debian server, chances are you will spend most of your time in a terminal session connected to your server through SSH.&lt;/p&gt;

&lt;p&gt;In this guide, we'll focus on setting up SSH keys for a vanilla Debian 9 installation. SSH keys provide an easy, secure way of logging into your server and are recommended for all users.&lt;/p&gt;

&lt;h2 id="step-1-—-create-the-rsa-key-pair"&gt;Step 1 — Create the RSA Key Pair&lt;/h2&gt;

&lt;p&gt;The first step is to create a key pair on the client machine (usually your computer):&lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;ssh-keygen
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By default &lt;code&gt;ssh-keygen&lt;/code&gt; will create a 2048-bit RSA key pair, which is secure enough for most use cases (you may optionally pass in the &lt;code&gt;-b 4096&lt;/code&gt; flag to create a larger 4096-bit key).&lt;/p&gt;

&lt;p&gt;After entering the command, you should see the following output:&lt;/p&gt;
&lt;pre class="code-pre  local-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Generating public/private rsa key pair.
Enter file in which to save the key (/&lt;span class="highlight"&gt;your_home&lt;/span&gt;/.ssh/id_rsa):
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Press enter to save the key pair into the &lt;code&gt;.ssh/&lt;/code&gt; subdirectory in your home directory, or specify an alternate path.&lt;/p&gt;

&lt;p&gt;If you had previously generated an SSH key pair, you may see the following prompt:&lt;/p&gt;
&lt;pre class="code-pre  local-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;/home/&lt;span class="highlight"&gt;your_home&lt;/span&gt;/.ssh/id_rsa already exists.
Overwrite (y/n)?
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you choose to overwrite the key on disk, you will &lt;strong&gt;not&lt;/strong&gt; be able to authenticate using the previous key anymore. Be very careful when selecting yes, as this is a destructive process that cannot be reversed.&lt;/p&gt;

&lt;p&gt;You should then see the following prompt:&lt;/p&gt;
&lt;pre class="code-pre  local-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Enter passphrase (empty for no passphrase):
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here you optionally may enter a secure passphrase, which is highly recommended. A passphrase adds an additional layer of security to prevent unauthorized users from logging in. To learn more about security, consult our tutorial on &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-configure-ssh-key-based-authentication-on-a-linux-server"&gt;How To Configure SSH Key-Based Authentication on a Linux Server&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You should then see the following output:&lt;/p&gt;
&lt;pre class="code-pre  local-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Your identification has been saved in /&lt;span class="highlight"&gt;your_home&lt;/span&gt;/.ssh/id_rsa.
Your public key has been saved in /&lt;span class="highlight"&gt;your_home&lt;/span&gt;/.ssh/id_rsa.pub.
The key fingerprint is:
a9:49:2e:2a:5e:33:3e:a9:de:4e:77:11:58:b6:90:26 username@remote_host
The key's randomart image is:
+--[ RSA 2048]----+
|     ..o         |
|   E o= .        |
|    o. o         |
|        ..       |
|      ..S        |
|     o o.        |
|   =o.+.         |
|. =++..          |
|o=++.            |
+-----------------+
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You now have a public and private key that you can use to authenticate. The next step is to place the public key on your server so that you can use SSH-key-based authentication to log in.&lt;/p&gt;

&lt;h2 id="step-2-—-copy-the-public-key-to-debian-server"&gt;Step 2 — Copy the Public Key to Debian Server&lt;/h2&gt;

&lt;p&gt;The quickest way to copy your public key to the Debian host is to use a utility called &lt;code&gt;ssh-copy-id&lt;/code&gt;. Due to its simplicity, this method is highly recommended if available. If you do not have &lt;code&gt;ssh-copy-id&lt;/code&gt; available to you on your client machine, you may use one of the two alternate methods provided in this section (copying via password-based SSH, or manually copying the key).&lt;/p&gt;

&lt;h3 id="copying-public-key-using-ssh-copy-id"&gt;Copying Public Key Using &lt;code&gt;ssh-copy-id&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;ssh-copy-id&lt;/code&gt; tool is included by default in many operating systems, so you may have it available on your local system. For this method to work, you must already have password-based SSH access to your server.&lt;/p&gt;

&lt;p&gt;To use the utility, you simply need to specify the remote host that you would like to connect to and the user account that you have password SSH access to. This is the account to which your public SSH key will be copied.&lt;/p&gt;

&lt;p&gt;The syntax is:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;ssh-copy-id &lt;span class="highlight"&gt;username&lt;/span&gt;@&lt;span class="highlight"&gt;remote_host&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You may see the following message:&lt;/p&gt;
&lt;pre class="code-pre  local-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;The authenticity of host '&lt;span class="highlight"&gt;203.0.113.1&lt;/span&gt; (&lt;span class="highlight"&gt;203.0.113.1&lt;/span&gt;)' can't be established.
ECDSA key fingerprint is fd:fd:d4:f9:77:fe:73:84:e1:55:00:ad:d6:6d:22:fe.
Are you sure you want to continue connecting (yes/no)? &lt;span class="highlight"&gt;yes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This means that your local computer does not recognize the remote host. This will happen the first time you connect to a new host.  Type "yes" and press &lt;code&gt;ENTER&lt;/code&gt; to continue.&lt;/p&gt;

&lt;p&gt;Next, the utility will scan your local account for the &lt;code&gt;id_rsa.pub&lt;/code&gt; key that we created earlier. When it finds the key, it will prompt you for the password of the remote user's account:&lt;/p&gt;
&lt;pre class="code-pre  local-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
&lt;span class="highlight"&gt;username&lt;/span&gt;@&lt;span class="highlight"&gt;203.0.113.1&lt;/span&gt;'s password:
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Type in the password (your typing will not be displayed for security purposes) and press &lt;code&gt;ENTER&lt;/code&gt;. The utility will connect to the account on the remote host using the password you provided. It will then copy the contents of your &lt;code&gt;~/.ssh/id_rsa.pub&lt;/code&gt; key into a file in the remote account's home &lt;code&gt;~/.ssh&lt;/code&gt; directory called &lt;code&gt;authorized_keys&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You should see the following output:&lt;/p&gt;
&lt;pre class="code-pre  local-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Number of key(s) added: 1

Now try logging into the machine, with:   "ssh '&lt;span class="highlight"&gt;username&lt;/span&gt;@&lt;span class="highlight"&gt;203.0.113.1&lt;/span&gt;'"
and check to make sure that only the key(s) you wanted were added.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At this point, your &lt;code&gt;id_rsa.pub&lt;/code&gt; key has been uploaded to the remote account. You can continue on to &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-set-up-ssh-keys-on-debian-9#step-3-%E2%80%94-authenticate-to-debian-server-using-ssh-keys"&gt;Step 3&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id="copying-public-key-using-ssh"&gt;Copying Public Key Using SSH&lt;/h3&gt;

&lt;p&gt;If you do not have &lt;code&gt;ssh-copy-id&lt;/code&gt; available, but you have password-based SSH access to an account on your server, you can upload your keys using a conventional SSH method.&lt;/p&gt;

&lt;p&gt;We can do this by using the &lt;code&gt;cat&lt;/code&gt; command to read the contents of the public SSH key on our local computer and piping that through an SSH connection to the remote server. &lt;/p&gt;

&lt;p&gt;On the other side, we can make sure that the &lt;code&gt;~/.ssh&lt;/code&gt; directory exists and has the correct permissions under the account we’re using.&lt;/p&gt;

&lt;p&gt;We can then output the content we piped over into a file called &lt;code&gt;authorized_keys&lt;/code&gt; within this directory. We’ll use the &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt; redirect symbol to append the content instead of overwriting it. This will let us add keys without destroying previously added keys.&lt;/p&gt;

&lt;p&gt;The full command looks like this:&lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cat ~/.ssh/id_rsa.pub | ssh &lt;span class="highlight"&gt;username&lt;/span&gt;@&lt;span class="highlight"&gt;remote_host&lt;/span&gt; "mkdir -p ~/.ssh &amp;amp;&amp;amp; touch ~/.ssh/authorized_keys &amp;amp;&amp;amp; chmod -R go= ~/.ssh &amp;amp;&amp;amp; cat &amp;gt;&amp;gt; ~/.ssh/authorized_keys"
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You may see the following message:&lt;/p&gt;
&lt;pre class="code-pre  local-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;The authenticity of host '&lt;span class="highlight"&gt;203.0.113.1&lt;/span&gt; (&lt;span class="highlight"&gt;203.0.113.1&lt;/span&gt;)' can't be established.
ECDSA key fingerprint is fd:fd:d4:f9:77:fe:73:84:e1:55:00:ad:d6:6d:22:fe.
Are you sure you want to continue connecting (yes/no)? &lt;span class="highlight"&gt;yes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This means that your local computer does not recognize the remote host. This will happen the first time you connect to a new host. Type "yes" and press &lt;code&gt;ENTER&lt;/code&gt; to continue.&lt;/p&gt;

&lt;p&gt;Afterwards, you should be prompted to enter the remote user account password:&lt;/p&gt;
&lt;pre class="code-pre  local-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;&lt;span class="highlight"&gt;username&lt;/span&gt;@&lt;span class="highlight"&gt;203.0.113.1&lt;/span&gt;'s password:
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After entering your password, the content of your &lt;code&gt;id_rsa.pub&lt;/code&gt; key will be copied to the end of the &lt;code&gt;authorized_keys&lt;/code&gt; file of the remote user's account.  Continue on to &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-set-up-ssh-keys-on-debian-9#step-3-%E2%80%94-authenticate-to-debian-server-using-ssh-keys"&gt;Step 3&lt;/a&gt; if this was successful.&lt;/p&gt;

&lt;h3 id="copying-public-key-manually"&gt;Copying Public Key Manually&lt;/h3&gt;

&lt;p&gt;If you do not have password-based SSH access to your server available, you will have to complete the above process manually.&lt;/p&gt;

&lt;p&gt;We will manually append the content of your &lt;code&gt;id_rsa.pub&lt;/code&gt; file to the &lt;code&gt;~/.ssh/authorized_keys&lt;/code&gt; file on your remote machine.&lt;/p&gt;

&lt;p&gt;To display the content of your &lt;code&gt;id_rsa.pub&lt;/code&gt; key, type this into your local computer:&lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cat ~/.ssh/id_rsa.pub
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will see the key's content, which should look something like this:&lt;/p&gt;
&lt;pre class="code-pre  local-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCqql6MzstZYh1TmWWv11q5O3pISj2ZFl9HgH1JLknLLx44+tXfJ7mIrKNxOOwxIxvcBF8PXSYvobFYEZjGIVCEAjrUzLiIxbyCoxVyle7Q+bqgZ8SeeM8wzytsY+dVGcBxF6N4JS+zVk5eMcV385gG3Y6ON3EG112n6d+SMXY0OEBIcO6x+PnUSGHrSgpBgX7Ks1r7xqFa7heJLLt2wWwkARptX7udSq05paBhcpB0pHtA1Rfz3K2B+ZVIpSDfki9UVKzT8JUmwW6NNzSgxUfQHGwnW7kj4jp4AT0VZk3ADw497M2G/12N0PPB5CnhHf7ovgy6nL1ikrygTKRFmNZISvAcywB9GVqNAVE+ZHDSCuURNsAInVzgYo9xgJDW8wUw2o8U77+xiFxgI5QSZX3Iq7YLMgeksaO4rBJEa54k8m5wEiEE1nUhLuJ0X/vh2xPff6SQ1BL/zkOhvJCACK6Vb15mDOeCSq54Cr7kvS46itMosi/uS66+PujOO+xt/2FWYepz6ZlN70bRly57Q06J+ZJoc9FfBCbCyYH7U/ASsmY095ywPsBo1XQ9PqhnN1/YOorJ068foQDNVpm146mUpILVxmq41Cj55YKHEazXGsdBIbXWhcrRf4G2fJLRcGUr9q8/lERo9oxRm5JFX6TCmj6kmiFqv+Ow9gI0x8GvaQ== demo@test
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Access your remote host using whichever method you have available. &lt;/p&gt;

&lt;p&gt;Once you have access to your account on the remote server, you should make sure the &lt;code&gt;~/.ssh&lt;/code&gt; directory exists. This command will create the directory if necessary, or do nothing if it already exists:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;mkdir -p ~/.ssh
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, you can create or modify the &lt;code&gt;authorized_keys&lt;/code&gt; file within this directory. You can add the contents of your &lt;code&gt;id_rsa.pub&lt;/code&gt; file to the end of the &lt;code&gt;authorized_keys&lt;/code&gt; file, creating it if necessary, using this command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;echo &lt;span class="highlight"&gt;public_key_string&lt;/span&gt; &amp;gt;&amp;gt; ~/.ssh/authorized_keys
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the above command, substitute the &lt;code&gt;&lt;span class="highlight"&gt;public_key_string&lt;/span&gt;&lt;/code&gt; with the output from the &lt;code&gt;cat ~/.ssh/id_rsa.pub&lt;/code&gt; command that you executed on your local system. It should start with &lt;code&gt;ssh-rsa AAAA...&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Finally, we’ll ensure that the &lt;code&gt;~/.ssh&lt;/code&gt; directory and &lt;code&gt;authorized_keys&lt;/code&gt; file have the appropriate permissions set:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;chmod -R go= ~/.ssh
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This recursively removes all “group” and “other” permissions for the &lt;code&gt;~/.ssh/&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;If you’re using the &lt;code&gt;root&lt;/code&gt; account to set up keys for a user account, it’s also important that the &lt;code&gt;~/.ssh&lt;/code&gt; directory belongs to the user and not to &lt;code&gt;root&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;chown -R &lt;span class="highlight"&gt;sammy&lt;/span&gt;:&lt;span class="highlight"&gt;sammy&lt;/span&gt; ~/.ssh
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this tutorial our user is named &lt;span class="highlight"&gt;sammy&lt;/span&gt; but you should substitute the appropriate username into the above command.&lt;/p&gt;

&lt;p&gt;We can now attempt passwordless authentication with our Debian server.&lt;/p&gt;

&lt;h2 id="step-3-—-authenticate-to-debian-server-using-ssh-keys"&gt;Step 3 — Authenticate to Debian Server Using SSH Keys&lt;/h2&gt;

&lt;p&gt;If you have successfully completed one of the procedures above, you should be able to log into the remote host &lt;em&gt;without&lt;/em&gt; the remote account's password.&lt;/p&gt;

&lt;p&gt;The basic process is the same:&lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;ssh &lt;span class="highlight"&gt;username&lt;/span&gt;@&lt;span class="highlight"&gt;remote_host&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If this is your first time connecting to this host (if you used the last method above), you may see something like this:&lt;/p&gt;
&lt;pre class="code-pre  local-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;The authenticity of host '&lt;span class="highlight"&gt;203.0.113.1&lt;/span&gt; (&lt;span class="highlight"&gt;203.0.113.1&lt;/span&gt;)' can't be established.
ECDSA key fingerprint is fd:fd:d4:f9:77:fe:73:84:e1:55:00:ad:d6:6d:22:fe.
Are you sure you want to continue connecting (yes/no)? &lt;span class="highlight"&gt;yes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This means that your local computer does not recognize the remote host. Type "yes" and then press &lt;code&gt;ENTER&lt;/code&gt; to continue.&lt;/p&gt;

&lt;p&gt;If you did not supply a passphrase for your private key, you will be logged in immediately. If you supplied a passphrase for the private key when you created the key, you will be prompted to enter it now (note that your keystrokes will not display in the terminal session for security). After authenticating, a new shell session should open for you with the configured account on the Debian server.&lt;/p&gt;

&lt;p&gt;If key-based authentication was successful, continue on to learn how to further secure your system by disabling password authentication.&lt;/p&gt;

&lt;h2 id="step-4-—-disable-password-authentication-on-your-server"&gt;Step 4 — Disable Password Authentication on your Server&lt;/h2&gt;

&lt;p&gt;If you were able to log into your account using SSH without a password, you have successfully configured SSH-key-based authentication to your account. However, your password-based authentication mechanism is still active, meaning that your server is still exposed to brute-force attacks.&lt;/p&gt;

&lt;p&gt;Before completing the steps in this section, make sure that you either have SSH-key-based authentication configured for the root account on this server, or preferably, that you have SSH-key-based authentication configured for a non-root account on this server with &lt;code&gt;sudo&lt;/code&gt; privileges. This step will lock down password-based logins, so ensuring that you will still be able to get administrative access is crucial.&lt;/p&gt;

&lt;p&gt;Once you've confirmed that your remote account has administrative privileges, log into your remote server with SSH keys, either as root or with an account with &lt;code&gt;sudo&lt;/code&gt; privileges. Then, open up the SSH daemon's configuration file:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/ssh/sshd_config
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Inside the file, search for a directive called &lt;code&gt;PasswordAuthentication&lt;/code&gt;. This may be commented out.  Uncomment the line and set the value to "no". This will disable your ability to log in via SSH using account passwords:&lt;/p&gt;
&lt;div class="code-label " title="/etc/ssh/sshd_config"&gt;/etc/ssh/sshd_config&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;...
PasswordAuthentication no
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save and close the file when you are finished by pressing &lt;code&gt;CTRL&lt;/code&gt; + &lt;code&gt;X&lt;/code&gt;, then &lt;code&gt;Y&lt;/code&gt; to confirm saving the file, and finally &lt;code&gt;ENTER&lt;/code&gt; to exit nano. To actually implement these changes, we need to restart the &lt;code&gt;sshd&lt;/code&gt; service:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl restart ssh
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As a precaution, open up a new terminal window and test that the SSH service is functioning correctly before closing this session:&lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;ssh &lt;span class="highlight"&gt;username&lt;/span&gt;@&lt;span class="highlight"&gt;remote_host&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once you have verified your SSH service, you can safely close all current server sessions. &lt;/p&gt;

&lt;p&gt;The SSH daemon on your Debian server now only responds to SSH keys. Password-based authentication has successfully been disabled.&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;You should now have SSH-key-based authentication configured on your server, allowing you to sign in without providing an account password.&lt;/p&gt;

&lt;p&gt;If you'd like to learn more about working with SSH, take a look at our &lt;a href="https://www.digitalocean.com/community/tutorials/ssh-essentials-working-with-ssh-servers-clients-and-keys"&gt;SSH Essentials Guide&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/a-rede-do-kubernetes-nos-bastidores-pt</id>
    <published>2018-08-27T20:43:42Z</published>
    <updated>2018-08-27T20:49:43Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/a-rede-do-kubernetes-nos-bastidores-pt"/>
    <title>A Rede do Kubernetes nos Bastidores</title>
    <content type="html">&lt;h3 id="introdução"&gt;Introdução&lt;/h3&gt;

&lt;p&gt;O Kubernetes é um poderoso sistema de orquestração de container que pode gerenciar o deployment e a operação de aplicações containerizadas em um cluster de servidores. Além de coordenar as cargas de trabalho do container, o Kubernetes fornece a infraestrutura e as ferramentas necessárias para manter a conectividade de rede entre suas aplicações e serviços.&lt;/p&gt;

&lt;p&gt;A &lt;a href="https://kubernetes.io/docs/concepts/cluster-administration/networking/"&gt;Documentação de Rede do Cluster do Kubernetes&lt;/a&gt; afirma que os requisitos básicos de uma rede Kubernetes são:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;todos os containers podem se comunicar com todos os outros containers sem NAT&lt;/li&gt;
&lt;li&gt;todos os nodes podem se comunicar com todos os containers (e vice-versa) sem NAT&lt;/li&gt;
&lt;li&gt;o IP com o qual um container se vê é o mesmo IP que os outros o veem&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Neste artigo, discutiremos como o Kubernetes satisfaz esses requisitos de rede dentro de um cluster: como os dados se movem dentro de um pod, entre pods e entre nodes.&lt;/p&gt;

&lt;p&gt;Também mostraremos como um &lt;strong&gt;Serviço&lt;/strong&gt; do Kubernetes pode fornecer um único endereço IP estático e uma entrada de DNS para uma aplicação, facilitando a comunicação com serviços que podem ser distribuídos entre vários pods de dimensionamento e deslocamento constantes.&lt;/p&gt;

&lt;p&gt;Se você não estiver familiarizado com a terminologia dos pods e nodes do Kubernetes ou com outros itens básicos, nosso artigo &lt;a href="https://www.digitalocean.com/community/tutorials/an-introduction-to-kubernetes"&gt;An Introduction to Kubernetes&lt;/a&gt; cobre a arquitetura geral e os componentes envolvidos. &lt;/p&gt;

&lt;p&gt;Primeiro, vamos dar uma olhada na situação da rede dentro de um único pod.&lt;/p&gt;

&lt;h2 id="a-rede-do-pod"&gt;A Rede do Pod&lt;/h2&gt;

&lt;p&gt;No Kubernetes, um &lt;em&gt;pod&lt;/em&gt; é a unidade mais básica de organização: um grupo de containers fortemente acoplados que estão todos intimamente relacionados e executam uma única função ou serviço.&lt;/p&gt;

&lt;p&gt;Em termos de rede, o Kubernetes trata pods de maneira semelhante a uma máquina virtual tradicional ou a um único host físico: cada pod recebe um único endereço IP exclusivo, e todos os containers dentro do pod compartilham esse endereço e se comunicam entre si através da interface de loopback &lt;strong&gt;lo&lt;/strong&gt; usando o nome de host &lt;strong&gt;localhost&lt;/strong&gt;. Isso é conseguido atribuindo todos os containers do pod à mesma pilha de rede.&lt;/p&gt;

&lt;p&gt;Essa situação deve parecer familiar para qualquer pessoa que fez o deploy de vários serviços em um único host antes dos dias da containerização. Todos os serviços precisam usar uma porta exclusiva para ouvir, mas, por outro lado, a comunicação é descomplicada e tem pouca sobrecarga.&lt;/p&gt;

&lt;h2 id="a-rede-de-pod-para-pod"&gt;A Rede de Pod para Pod&lt;/h2&gt;

&lt;p&gt;A maioria dos clusters do Kubernetes precisará fazer deploy de vários pods por node. A comunicação de pod para pod pode ocorrer entre dois pods no mesmo node ou entre dois nodes diferentes.&lt;/p&gt;

&lt;h3 id="comunicação-pod-a-pod-em-um-node"&gt;Comunicação Pod a Pod em um Node&lt;/h3&gt;

&lt;p&gt;Em um único node, você pode ter vários pods que precisam se comunicar diretamente uns com os outros. Antes de rastrearmos a rota de um pacote entre os pods, vamos analisar a configuração de rede de um node. O diagrama a seguir fornece uma visão geral, que abordaremos em detalhes:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/k8s-networking/single.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Cada node tem uma interface de rede – &lt;strong&gt;eth0&lt;/strong&gt; neste exemplo – anexada à rede de clusters do Kubernetes. Essa interface fica dentro do namespace de rede &lt;strong&gt;root&lt;/strong&gt; do node. Este é o namespace padrão para dispositivos de rede no Linux.&lt;/p&gt;

&lt;p&gt;Assim como os namespaces de processo permitem que os containers isolem as  aplicações em execução umas das outras, namespaces de rede isolam dispositivos de rede tais como interfaces e bridges. Cada pod em um node é atribuído ao seu próprio namespace de rede isolado.&lt;/p&gt;

&lt;p&gt;Os namespaces de pod são conectados de volta ao namespace &lt;strong&gt;root&lt;/strong&gt; com um &lt;em&gt;par ethernet virtual&lt;/em&gt;, essencialmente um pipe entre os dois namespaces com uma interface em cada extremidade (aqui estamos utilizando &lt;strong&gt;veth1&lt;/strong&gt; no namespace &lt;strong&gt;root&lt;/strong&gt; e &lt;strong&gt;eth0&lt;/strong&gt; dentro do pod).&lt;/p&gt;

&lt;p&gt;Finalmente, os pods são conectados entre si e à interface &lt;strong&gt;eth0&lt;/strong&gt; do node através de uma bridge, &lt;strong&gt;br0&lt;/strong&gt; (seu node pode usar algo como &lt;strong&gt;cbr0&lt;/strong&gt; ou &lt;strong&gt;docker0&lt;/strong&gt;). Uma bridge funciona essencialmente como um switch Ethernet físico, usando ARP (protocolo de resolução de endereço) ou roteamento baseado em IP para procurar outras interfaces locais para onde direcionar o tráfego.&lt;/p&gt;

&lt;p&gt;Agora vamos rastrear um pacote do &lt;strong&gt;pod1&lt;/strong&gt; para o &lt;strong&gt;pod2&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;pod1&lt;/strong&gt; cria um pacote com o IP do &lt;strong&gt;pod2&lt;/strong&gt; como seu destino&lt;/li&gt;
&lt;li&gt;O pacote trafega pelo par de ethernet virtual para o namespace root da rede&lt;/li&gt;
&lt;li&gt;O pacote continua até a bridge &lt;strong&gt;br0&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Como o pod de destino está no mesmo node, a bridge envia o pacote para o par de ethernet virtual do &lt;strong&gt;pod2&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;O pacote trafega através do par de ethernet virtual, no namespace de rede do &lt;strong&gt;pod2&lt;/strong&gt; e na interface de rede &lt;strong&gt;eth0&lt;/strong&gt; do pod.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Agora que rastreamos um pacote de pod para pod dentro de um node, vamos ver como o tráfego do pod viaja entre nodes.&lt;/p&gt;

&lt;h3 id="comunicação-pod-para-pod-entre-dois-nodes"&gt;Comunicação Pod para Pod entre dois Nodes&lt;/h3&gt;

&lt;p&gt;Como cada pod em um cluster tem um IP exclusivo e cada pod pode se comunicar diretamente com todos os outros pods, um pacote que se move entre os pods em dois nodes distintos é muito semelhante ao cenário anterior.&lt;/p&gt;

&lt;p&gt;Vamos rastrear um pacote do &lt;strong&gt;pod1&lt;/strong&gt; para o &lt;strong&gt;pod3&lt;/strong&gt;, que está em outro node:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/k8s-networking/double.png" alt=""&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;pod1&lt;/strong&gt; cria um pacote com o IP do &lt;strong&gt;pod3&lt;/strong&gt; como seu destino&lt;/li&gt;
&lt;li&gt;O pacote trafega pelo par de ethernet virtual para o namespace root da rede&lt;/li&gt;
&lt;li&gt;O pacote continua até a bridge &lt;strong&gt;br0&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;A bridge não encontra nenhuma interface local para onde rotear, assim o pacote é enviado para a rota padrão via &lt;strong&gt;eth0&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Opcional:&lt;/em&gt; se o seu cluster exigir uma sobreposição de rede para rotear corretamente os pacotes para os nodes, o pacote poderá ser encapsulado em um pacote VXLAN (ou outra técnica de virtualização de rede) antes de ir para a rede. Alternativamente, a própria rede pode ser configurada com as rotas estáticas adequadas, nesse caso, o pacote trafega para eth0 e sai da rede inalterado.&lt;/li&gt;
&lt;li&gt;O pacote entra na rede do cluster e é roteado para o node correto.&lt;/li&gt;
&lt;li&gt;O pacote entra no node de destino na &lt;strong&gt;eth0&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Opcional:&lt;/em&gt; se o seu pacote foi encapsulado, ele será desencapsulado neste momento&lt;/li&gt;
&lt;li&gt;O pacote continua para a bridge &lt;strong&gt;br0&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;A bridge encaminha o pacote para o par de ethernet virtual do pod de destino&lt;/li&gt;
&lt;li&gt;O pacote passa pelo par de ethernet virtual para a interface &lt;strong&gt;eth0&lt;/strong&gt; do pod&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Agora que estamos familiarizados com a forma como os pacotes são roteados por meio dos endereços IP do pod, vamos dar uma olhada nos &lt;em&gt;serviços&lt;/em&gt; do Kubernetes e em como eles se baseiam nessa infraestrutura.&lt;/p&gt;

&lt;h2 id="a-rede-de-pod-para-serviço"&gt;A Rede de Pod para Serviço&lt;/h2&gt;

&lt;p&gt;Seria difícil enviar tráfego para uma aplicação específica usando apenas IPs de pod, pois a natureza dinâmica de um cluster do Kubernetes significa que os pods podem ser movidos, reiniciados, atualizados ou redimensionados para dentro e para fora. Além disso, alguns serviços terão muitas réplicas, por isso precisamos de alguma forma de balancear a carga entre eles.&lt;/p&gt;

&lt;p&gt;O Kubernetes resolve esse problema com os Serviços. Um Serviço é um objeto da API que mapeia um único IP virtual (VIP) para um conjunto de IPs de pod. Além disso, o Kubernetes fornece uma entrada de DNS para o nome de cada serviço e IP virtual, para que os serviços possam ser facilmente acessados por nome.  &lt;/p&gt;

&lt;p&gt;O mapeamento de IPs virtuais para IPs de pods dentro do cluster é coordenado pelo processo &lt;code&gt;kube-proxy&lt;/code&gt; em cada node. Esse processo configura ou o &lt;a href="https://www.digitalocean.com/community/tutorials/a-deep-dive-into-iptables-and-netfilter-architecture"&gt;iptables&lt;/a&gt; ou IPVS para traduzir automaticamente os VIPs em IPs de pods antes de enviar o pacote para a rede do cluster. Conexões individuais são rastreadas para que os pacotes possam ser devidamente decodificados quando retornarem. O IPVS e o iptables podem fazer o balanceamento de carga de um único IP virtual de serviço em vários IPs de pods, embora o IPVS tenha muito mais flexibilidade nos algoritmos de balanceamento de carga que ele pode usar.&lt;/p&gt;

&lt;p&gt;&lt;span class='note'&gt;&lt;strong&gt;Nota:&lt;/strong&gt; Este processo de rastreamento de tradução e de conexão acontece inteiramente no kernel do Linux. O kube-proxy lê a API do Kubernetes e atualiza o ip no iptables e IPVS, mas ele não está no caminho dos dados para pacotes individuais. Isso é mais eficiente e de melhor desempenho do que as versões anteriores do kube-proxy, que funcionava como um proxy de mando do usuário.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;Vamos seguir a rota que um pacote leva de um pod, &lt;strong&gt;pod1&lt;/strong&gt; novamente, para um serviço, &lt;strong&gt;service1&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/k8s-networking/double-service.png" alt=""&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;pod1&lt;/strong&gt; cria um pacote com o IP do &lt;strong&gt;service1&lt;/strong&gt; como seu destino&lt;/li&gt;
&lt;li&gt;O pacote trafega pelo par de ethernet virtual para o namespace root da rede&lt;/li&gt;
&lt;li&gt;O pacote continua até a bridge &lt;strong&gt;br0&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;A bridge não encontra nenhuma interface local para onde rotear o pacote, assim o pacote é enviado para a rota padrão via &lt;strong&gt;eth0&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Iptables ou IPVS, configurados pelo &lt;code&gt;kube-proxy&lt;/code&gt;, acham o IP de destino do pacote e o traduzem de um IP virtual para um dos IPs do pod de serviço, usando quaisquer algoritmos de balanceamento de carga disponíveis ou especificados&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Opcional:&lt;/em&gt; seu pacote pode ser encapsulado neste ponto, como discutido na seção anterior&lt;/li&gt;
&lt;li&gt;O pacote entra na rede do cluster e é roteado para o node correto.&lt;/li&gt;
&lt;li&gt;O pacote entra no node de destino na &lt;strong&gt;eth0&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Opcional:&lt;/em&gt; se o seu pacote foi encapsulado, ele será desencapsulado neste momento&lt;/li&gt;
&lt;li&gt;O pacote continua até a bridge &lt;strong&gt;br0&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;O pacote é enviado para o par de ethernet virtual via &lt;strong&gt;veth1&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;O pacote passa pelo par de ethernet virtual e entra no namespace de rede do pod através de sua interface de rede &lt;strong&gt;eth0&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Quando o pacote retorna para o &lt;strong&gt;node1&lt;/strong&gt;, a tradução de VIP para IP do pod será revertida, e o pacote retornará através da bridge e da interface virtual para o pod correto.&lt;/p&gt;

&lt;h2 id="conclusão"&gt;Conclusão&lt;/h2&gt;

&lt;p&gt;Neste artigo, analisamos a infraestrutura de rede interna de um cluster do Kubernetes. Discutimos os blocos construtivos que compõem a rede e detalhamos a jornada salto-por-salto de pacotes em diferentes cenários.&lt;/p&gt;

&lt;p&gt;Para mais informações sobre o Kubernetes, dê uma olhada na &lt;a href="https://www.digitalocean.com/community/tags/kubernetes?type=tutorials"&gt;tag para nossos tutoriais de Kubernetes&lt;/a&gt; e a &lt;a href="https://kubernetes.io/docs/home/"&gt;documentação oficial do Kubernetes&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/como-inspecionar-a-rede-do-kubernetes-pt</id>
    <published>2018-08-27T20:40:28Z</published>
    <updated>2018-08-29T20:40:51Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/como-inspecionar-a-rede-do-kubernetes-pt"/>
    <title>Como Inspecionar a Rede do Kubernetes</title>
    <content type="html">&lt;h3 id="introducão"&gt;Introducão&lt;/h3&gt;

&lt;p&gt;O Kubernetes é um sistema de orquestração de container que pode gerenciar aplicações containerizadas em um cluster de nodes de servidores. A manutenção da conectividade de rede entre todos os containers em um cluster requer algumas técnicas avançadas de rede. Neste artigo vamos cobrir brevemente algumas ferramentas e técnicas para inspecionar essa configuração de rede.&lt;/p&gt;

&lt;p&gt;Estas ferramentas podem ser úteis se você estiver debugando problemas de conectividade, investigando problemas de taxa de transferência de rede, ou explorando o Kubernetes para aprender como ele funciona.&lt;/p&gt;

&lt;p&gt;Se você quiser aprender mais sobre o Kubernetes em geral, nosso guia &lt;a href="https://www.digitalocean.com/community/tutorials/an-introduction-to-kubernetes"&gt;An Introduction to Kubernetes&lt;/a&gt; cobre o básico. Para uma visão específica de rede do Kubernetes, por favor leia &lt;a href="https://www.digitalocean.com/community/tutorials/kubernetes-networking-under-the-hood"&gt;Kubernetes Networking Under the Hood&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id="começando"&gt;Começando&lt;/h2&gt;

&lt;p&gt;Este tutorial irá assumir que você tem um cluster Kubernetes, com o &lt;code&gt;kubectl&lt;/code&gt; instalado localmente e configurado para se conectar ao cluster.&lt;/p&gt;

&lt;p&gt;As seções seguintes contém muitos comandos que se destinam a serem executados em um node do Kubernetes. Eles se parecerão com isso:&lt;/p&gt;
&lt;pre class="code-pre super_user"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="#"&gt;echo 'este é um comando de node'
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Comandos que devem ser executados em sua máquina local terão a seguinte aparência:&lt;/p&gt;
&lt;pre class="code-pre super_user local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="#"&gt;echo 'este é um comando local'
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span class='note'&gt;&lt;strong&gt;Nota:&lt;/strong&gt; A maioria dos comandos neste tutorial precisará ser executada como usuário root. Se em vez disso você usar um usuário habilitado para o sudo em seus nodes de Kubernetes, por favor adicione &lt;code&gt;sudo&lt;/code&gt; para executar comandos quando necessário.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;

&lt;h2 id="encontrando-o-ip-do-cluster-de-um-pod"&gt;Encontrando o IP do Cluster de um Pod&lt;/h2&gt;

&lt;p&gt;Para encontrar o endereço IP de um pod do Kubermetes, utilize o comando &lt;code&gt;kubectl get pod&lt;/code&gt; em sua máquina local, com a opção &lt;code&gt;-o wide&lt;/code&gt;. Esta oção irá listar mais informações, incluindo o node onde o pod reside, e o IP do cluster do pod.&lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl get pod -o wide
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre  local-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;
NAME                           READY     STATUS    RESTARTS   AGE       IP            NODE
hello-world-5b446dd74b-7c7pk   1/1       Running   0          22m       10.244.18.4   node-one
hello-world-5b446dd74b-pxtzt   1/1       Running   0          22m       10.244.3.4    node-two
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A coluna &lt;strong&gt;IP&lt;/strong&gt; irá conter o endereço IP local do cluster para cada pod.&lt;/p&gt;

&lt;p&gt;Se você não vir o pod que está procurando, certifique-se de que você está no namespace certo. Você pode listar todos os pods em todos os namespaces adicionando o flag &lt;code&gt;--all-namespaces&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id="encontrando-o-ip-de-um-serviço"&gt;Encontrando o IP de um Serviço&lt;/h2&gt;

&lt;p&gt;Você pode também encontrar o IP de um serviço utilizando o &lt;code&gt;kubectl&lt;/code&gt;. Neste caso iremos listar todos os serviços em todos os namespaces:&lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl get service --all-namespaces
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre  local-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;
Output
NAMESPACE     NAME                       TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)         AGE
default       kubernetes                 ClusterIP   10.32.0.1       &amp;lt;none&amp;gt;        443/TCP         6d
kube-system   csi-attacher-doplugin      ClusterIP   10.32.159.128   &amp;lt;none&amp;gt;        12345/TCP       6d
kube-system   csi-provisioner-doplugin   ClusterIP   10.32.61.61     &amp;lt;none&amp;gt;        12345/TCP       6d
kube-system   kube-dns                   ClusterIP   10.32.0.10      &amp;lt;none&amp;gt;        53/UDP,53/TCP   6d
kube-system   kubernetes-dashboard       ClusterIP   10.32.226.209   &amp;lt;none&amp;gt;        443/TCP         6d
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;O IP do serviço pode ser encontrado na coluna &lt;strong&gt;CLUSTER-IP&lt;/strong&gt;.&lt;/p&gt;

&lt;h2 id="encontrando-e-inserindo-namespaces-de-rede-do-pod"&gt;Encontrando e Inserindo Namespaces de Rede do Pod&lt;/h2&gt;

&lt;p&gt;Cada pod do Kubernetes é atribuído ao seu próprio namespace de rede. Namespaces de rede (ou netns) são primitivas de rede do Linux que fornecem isolação entre dispositivos de rede.&lt;/p&gt;

&lt;p&gt;Isto pode ser útil para executar comandos a partir do netns do pod, para verificar resolução de DNS ou conectividade geral de rede. Para fazer isto, precisamos primeiro olhar para o ID de processo de um dos containers em um pod. Para o Docker, podemos fazer isto com uma série de dois comandos. Primeiro, liste os containers que estão executando em um node:&lt;/p&gt;
&lt;pre class="code-pre super_user"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="#"&gt;docker ps
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;
CONTAINER ID        IMAGE                                   COMMAND                  CREATED             STATUS              PORTS               NAMES
&lt;span class="highlight"&gt;173ee46a3926&lt;/span&gt;        gcr.io/google-samples/node-hello        "/bin/sh -c 'node se…"   9 days ago          Up 9 days                               &lt;span class="highlight"&gt;k8s_hello-world_hello-world-5b446dd74b-pxtzt_default_386a9073-7e35-11e8-8a3d-bae97d2c1afd_0&lt;/span&gt;
11ad51cb72df        k8s.gcr.io/pause-amd64:3.1              "/pause"                 9 days ago          Up 9 days                               k8s_POD_hello-world-5b446dd74b-pxtzt_default_386a9073-7e35-11e8-8a3d-bae97d2c1afd_0
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Encontre o &lt;strong&gt;container ID&lt;/strong&gt; ou &lt;strong&gt;name&lt;/strong&gt; de qualquer container no pod que você está interessado. Na saída acima estamos mostrando dois containers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;O primeiro container é o app &lt;code&gt;hello-world&lt;/code&gt; executando no pod &lt;code&gt;hello-world&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;O segundo é um container &lt;em&gt;pause&lt;/em&gt; executando no pod &lt;code&gt;hello-world&lt;/code&gt;. Este container existe apenas para manter o namespace de rede do pod&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Para obter o ID de processo de um dos containers, tome nota do container ID ou name, e utilize-o no seguinte comando &lt;code&gt;docker&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre super_user"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="#"&gt;docker inspect --format '{{ .State.Pid }}' &lt;span class="highlight"&gt;container-id-or-name&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;&lt;span class="highlight"&gt;14552&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Um ID de processo (ou PID) será a saída. Agora podemos utilizar o programa &lt;code&gt;nsenter&lt;/code&gt; para executar um comando no namespace de rede do processo: &lt;/p&gt;
&lt;pre class="code-pre super_user"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="#"&gt;nsenter -t &lt;span class="highlight"&gt;your-container-pid&lt;/span&gt; -n &lt;span class="highlight"&gt;ip addr&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Certifique-se de utilizar seu próprio PID, e substitua &lt;code&gt;ip addr&lt;/code&gt; pelo comando que você gostaria de executar dentro do namespace de rede do pod.&lt;/p&gt;

&lt;p&gt;&lt;span class='note'&gt;&lt;strong&gt;Nota:&lt;/strong&gt; Uma vantagem de se utilizar &lt;code&gt;nsenter&lt;/code&gt; para executar comandos no namespace do pod – versus a utilização de algo como &lt;code&gt;docker exec&lt;/code&gt; – é que você tem acesso a todos os comandos disponíveis no node, em vez do conjunto de comandos gralmente limitados instalados em containers.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;

&lt;h2 id="encontrando-a-interface-ethernet-virtual-de-um-pod"&gt;Encontrando a Interface Ethernet Virtual de um Pod&lt;/h2&gt;

&lt;p&gt;Cada namespace de rede do pod comunica-se com o netns raiz do node através de um pipe ethernet virtual. No lado do node, este pipe aparece como um dispositivo que geralmente começa com &lt;code&gt;veth&lt;/code&gt; e termina em um identificador único, tal como &lt;code&gt;veth77f2275&lt;/code&gt; ou &lt;code&gt;veth01&lt;/code&gt;. Dentro do pod este pipe aparece como &lt;code&gt;eth0&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Pode ser útil correlacionar qual dispositivo &lt;code&gt;veth&lt;/code&gt; está emparelhado com um pod em particular. Para fazer isto, vamos listar todos os dispositivos de rede no node, em seguida listar os dispositivos no namespace de rede do pod. Podemos correlacionar os números dos dispositivos entre as duas listas para fazer a conexão.&lt;/p&gt;

&lt;p&gt;Primeiro, execute &lt;code&gt;ip addr&lt;/code&gt; no namespace de rede do pod utilizando o &lt;code&gt;nsenter&lt;/code&gt;. Consulte a seção anterior &lt;a href="https://www.digitalocean.com/community/tutorials/como-inspecionar-a-rede-do-kubernetes-pt#encontrando-e-inserindo-namespaces-de-rede-do-pod"&gt;Encontrando e Inserindo Namespaces de Rede do Pod&lt;/a&gt; para detlahes de como fazer isto:&lt;/p&gt;
&lt;pre class="code-pre super_user"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="#"&gt;nsenter -t &lt;span class="highlight"&gt;pid-do-seu-container&lt;/span&gt; -n ip addr
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;
1: lo: &amp;lt;LOOPBACK,UP,LOWER_UP&amp;gt; mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
10: eth0@&lt;span class="highlight"&gt;if11&lt;/span&gt;: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1450 qdisc noqueue state UP group default
    link/ether 02:42:0a:f4:03:04 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 10.244.3.4/24 brd 10.244.3.255 scope global eth0
       valid_lft forever preferred_lft forever
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;O comando mostrará uma lista das interfaces do pod. Observe o número &lt;code&gt;if11&lt;/code&gt; depois de &lt;code&gt;eth0@&lt;/code&gt; na saída do exemplo. Isso significa que essa &lt;code&gt;eth0&lt;/code&gt; do pod está ligada à décima primeira interface  do node. Agora execute &lt;code&gt;ip addr&lt;/code&gt; no namespace padrão do node para listar suas interfaces:&lt;/p&gt;
&lt;pre class="code-pre super_user"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="#"&gt;ip addr
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;
1: lo: &amp;lt;LOOPBACK,UP,LOWER_UP&amp;gt; mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever

. . .

7: veth77f2275@if6: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1450 qdisc noqueue master docker0 state UP group default
    link/ether 26:05:99:58:0d:b9 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet6 fe80::2405:99ff:fe58:db9/64 scope link
       valid_lft forever preferred_lft forever
9: vethd36cef3@if8: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1450 qdisc noqueue master docker0 state UP group default
    link/ether ae:05:21:a2:9a:2b brd ff:ff:ff:ff:ff:ff link-netnsid 1
    inet6 fe80::ac05:21ff:fea2:9a2b/64 scope link
       valid_lft forever preferred_lft forever
&lt;span class="highlight"&gt;11&lt;/span&gt;: &lt;span class="highlight"&gt;veth4f7342d&lt;/span&gt;@if10: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1450 qdisc noqueue master docker0 state UP group default
    link/ether e6:4d:7b:6f:56:4c brd ff:ff:ff:ff:ff:ff link-netnsid 2
    inet6 fe80::e44d:7bff:fe6f:564c/64 scope link
       valid_lft forever preferred_lft forever
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A décima primeira interface é a &lt;code&gt;veth4f7342d&lt;/code&gt; nessa saída do exemplo. Este é o pipe ethernet virtual para o pod que estamos inevstigando.&lt;/p&gt;

&lt;h2 id="inspeção-do-rastreamento-de-conexão-do-conntrack"&gt;Inspeção do Rastreamento de Conexão do Conntrack&lt;/h2&gt;

&lt;p&gt;Antes da versão 1.11, o Kubernetes usava o iptables NAT e o módulo conntrack do kernel para rastrear conexões. Para listar todas as conexões sendo rastreadas atualmente, utilize o comando &lt;code&gt;conntrack&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre super_user"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="#"&gt;conntrack -L
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Para assitir continuamente por novas conexões, utilize o flag &lt;code&gt;-E&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre super_user"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="#"&gt;conntrack -E
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Para listar conexões controladas pelo conntrack a um endereço de destino específico, utilize o flag &lt;code&gt;-d&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre super_user"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="#"&gt;conntrack -L -d &lt;span class="highlight"&gt;10.32.0.1&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Se os seus nodes estão tendo problemas para fazer conexões confiáveis aos serviços, é possível que sua tabela de rastreamento de conexões esteja cheia e que novas conexões estejam sendo descartadas. Se é esse o caso você pode ver mensagens como as seguintes em seus logs de sistema:&lt;/p&gt;
&lt;div class="code-label " title="/var/log/syslog"&gt;/var/log/syslog&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;
Jul 12 15:32:11 worker-528 kernel: &lt;span class="highlight"&gt;nf_conntrack: table full, dropping packet.&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Há uma configuração do sysctl para o número máximo de conexões a serem rastreadas. Você pode listar o valor atual com o seguinte comando:&lt;/p&gt;
&lt;pre class="code-pre super_user"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="#"&gt;sysctl net.netfilter.nf_conntrack_max
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;
net.netfilter.nf_conntrack_max = 131072
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Para definir um novo valor, utilize o flag &lt;code&gt;-w&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre super_user"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="#"&gt;sysctl -w net.netfilter.nf_conntrack_max=&lt;span class="highlight"&gt;198000&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Para tornar essa configuração permanente, adicione-a ao arquivo &lt;code&gt;sysctl.conf&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-label " title="/etc/sysctl.conf"&gt;/etc/sysctl.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;
. . .
&lt;span class="highlight"&gt;net.ipv4.netfilter.ip_conntrack_max = 198000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="inspecionando-as-regras-do-iptables"&gt;Inspecionando as Regras do Iptables&lt;/h2&gt;

&lt;p&gt;Antes da versão 1.11, o Kubernetes usou o iptables NAT para implementar tradução de IP virtual e o balanceamento de carga para IPs de Serviço.&lt;/p&gt;

&lt;p&gt;Para fazer um dump de todas as regras iptables em um node, utilize o comando &lt;code&gt;iptables-save&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre super_user"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="#"&gt;iptables-save
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Como a saída pode ser longa, você pode querer redirecionar para um arquivo (&lt;code&gt;iptables-save &amp;gt; output.txt&lt;/code&gt;) ou um paginador (&lt;code&gt;iptables-save | less&lt;/code&gt;) para avaliar suas regras mais facilmente.&lt;/p&gt;

&lt;p&gt;Para listar apenas as regras NAT do Serviço do Kubernetes, utilize o comando &lt;code&gt;iptables&lt;/code&gt; e o flag &lt;code&gt;-L&lt;/code&gt; para especificar o canal correto:&lt;/p&gt;
&lt;pre class="code-pre super_user"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="#"&gt;iptables -t nat -L KUBE-SERVICES
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;
Chain KUBE-SERVICES (2 references)
target     prot opt source               destination
KUBE-SVC-TCOU7JCQXEZGVUNU  udp  --  anywhere             10.32.0.10           /* kube-system/kube-dns:dns cluster IP */ udp dpt:domain
KUBE-SVC-ERIFXISQEP7F7OF4  tcp  --  anywhere             10.32.0.10           /* kube-system/kube-dns:dns-tcp cluster IP */ tcp dpt:domain
KUBE-SVC-XGLOHA7QRQ3V22RZ  tcp  --  anywhere             10.32.226.209        /* kube-system/kubernetes-dashboard: cluster IP */ tcp dpt:https
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="consultando-o-dns-do-cluster"&gt;Consultando o DNS do Cluster&lt;/h2&gt;

&lt;p&gt;Uma maneira de fazer o debug da resolução de DNS do cluster é fazer o deploy de um container para debug com todas as feramentas que você precisa, em seguida utilize &lt;code&gt;kubectl&lt;/code&gt; para executar &lt;code&gt;nslookup&lt;/code&gt; nele. Isso é descrito na &lt;a href="https://kubernetes.io/docs/tasks/administer-cluster/dns-debugging-resolution/"&gt;documentação oficial do Kubernetes&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Outra maneira de consultar o DNS do cluster é a utilização do &lt;code&gt;dig&lt;/code&gt; e &lt;code&gt;nsenter&lt;/code&gt; a partir do node. Se o &lt;code&gt;dig&lt;/code&gt; não está instalado, pode-se instalar com o &lt;code&gt;apt&lt;/code&gt; em distribuições Linux baseadas em Debian.&lt;/p&gt;
&lt;pre class="code-pre super_user"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="#"&gt;apt install dnsutils
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Primeiro, encontre o IP do cluster do serviço &lt;strong&gt;kube-dns&lt;/strong&gt;:&lt;/p&gt;
&lt;pre class="code-pre super_user local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="#"&gt;kubectl get service -n kube-system kube-dns
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre  local-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;NAME       TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)         AGE
kube-dns   ClusterIP   &lt;span class="highlight"&gt;10.32.0.10&lt;/span&gt;   &amp;lt;none&amp;gt;        53/UDP,53/TCP   15d
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;O IP do cluster está destacado acima. Em seguida, vamos utilizar &lt;code&gt;nsenter&lt;/code&gt; para executar o &lt;code&gt;dig&lt;/code&gt; no namespace do container. Veja a seção &lt;a href="https://www.digitalocean.com/community/tutorials/como-inspecionar-a-rede-do-kubernetes-pt#encontrando-e-inserindo-namespaces-de-rede-do-pod"&gt;&lt;em&gt;Encontrando e Inserindo Namespaces de Rede do Pod&lt;/em&gt;&lt;/a&gt; para mais informações sobre isso.&lt;/p&gt;
&lt;pre class="code-pre super_user"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="#"&gt;nsenter -t &lt;span class="highlight"&gt;14346&lt;/span&gt; -n dig &lt;span class="highlight"&gt;kubernetes.default.svc.cluster.local&lt;/span&gt; @&lt;span class="highlight"&gt;10.32.0.10&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Este comando dig procura o nome de domínio completo do Serviço de &lt;strong&gt;&lt;span class="highlight"&gt;service-name.namespace.svc.cluster.local&lt;/span&gt;&lt;/strong&gt; e especifica o IP do serviço DNS do cluster (&lt;code&gt;@&lt;span class="highlight"&gt;10.32.0.10&lt;/span&gt;&lt;/code&gt;).&lt;/p&gt;

&lt;h2 id="olhando-para-os-detalhes-do-ipvs"&gt;Olhando para os Detalhes do IPVS&lt;/h2&gt;

&lt;p&gt;A partir do Kubernetes 1.11, o &lt;code&gt;kube-proxy&lt;/code&gt; pode configurar o IPVS para lidar com a tradução de IPs de serviços virtuais para IPs de pods. Você pode listar a tabela de tradução com &lt;code&gt;ipvsadm&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre super_user"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="#"&gt;ipvsadm -Ln
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -&amp;gt; RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  100.64.0.1:443 rr
  -&amp;gt; 178.128.226.86:443           Masq    1      0          0
TCP  100.64.0.10:53 rr
  -&amp;gt; 100.96.1.3:53                Masq    1      0          0
  -&amp;gt; 100.96.1.4:53                Masq    1      0          0
UDP  100.64.0.10:53 rr
  -&amp;gt; 100.96.1.3:53                Masq    1      0          0
  -&amp;gt; 100.96.1.4:53                Masq    1      0          0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Para mostrar um único IP de serviço, utilize a opção &lt;code&gt;-t&lt;/code&gt; e especifique o IP desejado:&lt;/p&gt;
&lt;pre class="code-pre super_user"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="#"&gt;ipvsadm -Ln -t &lt;span class="highlight"&gt;100.64.0.10:53&lt;/span&gt;
&lt;/li&gt;&lt;li class="line" prefix="#"&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;
Prot LocalAddress:Port Scheduler Flags
  -&amp;gt; RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  100.64.0.10:53 rr
  -&amp;gt; 100.96.1.3:53                Masq    1      0          0
  -&amp;gt; 100.96.1.4:53                Masq    1      0          0

&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="conclusão"&gt;Conclusão&lt;/h2&gt;

&lt;p&gt;Neste artigo, analisamos alguns comandos e técnicas para explorar e inspecionar os detalhes da rede do cluster do Kubernetes. Para mais informações sobre Kubernetes, dê uma olhada na nossa tag &lt;a href="https://www.digitalocean.com/community/tags/kubernetes?type=tutorials"&gt;de tutoriais de Kubernetes&lt;/a&gt; e na &lt;a href="https://kubernetes.io/docs/home/"&gt;documentação oficial do Kubernetes&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-use-node-js-and-github-webhooks-to-keep-remote-projects-in-sync</id>
    <published>2018-08-17T19:07:13Z</published>
    <updated>2018-08-31T14:46:18Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-use-node-js-and-github-webhooks-to-keep-remote-projects-in-sync"/>
    <title>How to Use Node.js and Github Webhooks to Keep Remote Projects in Sync</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;When working on a project with multiple developers, it can be frustrating when one person pushes to a repository and then another begins making changes on an outdated version of the code. Mistakes like these cost time, which makes it worthwhile to set up a script to keep your repositories in sync. You can also apply this method in a production environment to push hotfixes and other changes quickly. &lt;/p&gt;

&lt;p&gt;While other solutions exist to complete this specific task, writing your own script is a flexible option that leaves room for customization in the future.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com"&gt;GitHub&lt;/a&gt; lets you configure &lt;a href="https://developer.github.com/webhooks/"&gt;webhooks&lt;/a&gt; for your repositories, which are events that send HTTP requests when events happen. For example, you can use a webhook to notify you when someone creates a pull request or pushes new code.&lt;/p&gt;

&lt;p&gt;In this guide you will develop a &lt;a href="https://nodejs.org/"&gt;Node.js&lt;/a&gt; server that listens for a GitHub webhook notification whenever you or someone else pushes code to GitHub. This script will automatically update a repository on a remote server with the most recent version of the code, eliminating the need to log in to a server to pull new commits.&lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;To complete this tutorial, you will need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One Ubuntu 16.04 server set up by following &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-16-04"&gt;the Ubuntu 16.04 initial server setup guide&lt;/a&gt;, including a non-root user with &lt;code&gt;sudo&lt;/code&gt; privileges and a firewall.&lt;/li&gt;
&lt;li&gt;Git installed on your local machine. You can follow the tutorial &lt;a href="https://www.digitalocean.com/community/tutorials/contributing-to-open-source-getting-started-with-git"&gt;Contributing to Open Source: Getting Started with Git&lt;/a&gt; to install and set up Git on your computer.&lt;/li&gt;
&lt;li&gt;Node.js and &lt;code&gt;npm&lt;/code&gt; installed on the remote server using the official PPA, as explained explained in &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-node-js-on-ubuntu-16-04"&gt;How To Install Node.js on Ubuntu 16.04&lt;/a&gt;. Installing the distro-stable version is sufficient as it provides us with the recommended version without any additional configuration. &lt;/li&gt;
&lt;li&gt;A repository on Github that contains your project code. If you don't have a project in mind, feel free to &lt;a href="https://github.com/do-community/hello_hapi"&gt;fork this example&lt;/a&gt; which we'll use in the rest of the tutorial. &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="step-1-—-setting-up-a-webhook"&gt;Step 1 — Setting Up a Webhook&lt;/h2&gt;

&lt;p&gt;We'll start by configuring a webhook for your repository. This step is important because without it, Github doesn't know what events to send when things happen, or where to send them. We'll create the webhook first, and then create the server that will respond to its requests.&lt;/p&gt;

&lt;p&gt;Sign in to your GitHub account and navigate to the repository you wish to monitor. Click on the &lt;strong&gt;Settings&lt;/strong&gt; tab in the top menu bar on your repository's page, then click &lt;strong&gt;Webhooks&lt;/strong&gt; in the left navigation menu. Click &lt;strong&gt;Add Webhook&lt;/strong&gt; in the right corner and enter your account password if prompted. You'll see a page that looks like this:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/node_webhook_sync_1604/ZLBHSjF.png" alt="Webhooks Page"&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In the &lt;strong&gt;Payload URL&lt;/strong&gt; field, enter &lt;code&gt;http://&lt;span class="highlight"&gt;your_server_ip&lt;/span&gt;:8080&lt;/code&gt;.  This is the address and port of the Node.js server we'll write soon.&lt;/li&gt;
&lt;li&gt;Change the &lt;strong&gt;Content type&lt;/strong&gt; to &lt;code&gt;application/json&lt;/code&gt;. The script we will write will expect JSON data and won't be able to understand other data types. &lt;/li&gt;
&lt;li&gt;For &lt;strong&gt;Secret&lt;/strong&gt;, enter a secret password for this webhook. You'll use this secret in your Node.js server to validate requests and make sure they came from GitHub.&lt;br&gt;&lt;/li&gt;
&lt;li&gt;For &lt;strong&gt;Which events would you like to trigger this webhook&lt;/strong&gt;, select &lt;strong&gt;just the push event&lt;/strong&gt;. We only need the push event since that is when code is updated and needs to be synced to our server.&lt;br&gt;&lt;/li&gt;
&lt;li&gt;Select the &lt;strong&gt;Active&lt;/strong&gt; checkbox. &lt;/li&gt;
&lt;li&gt;Review the fields and click &lt;strong&gt;Add webhook&lt;/strong&gt; to create it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The ping will fail at first, but rest assured your webhook is now configured. Now let's get the repository cloned to the server.&lt;/p&gt;

&lt;h2 id="step-2-—-cloning-the-repository-to-the-server"&gt;Step 2 — Cloning the Repository to the Server&lt;/h2&gt;

&lt;p&gt;Our script can update a repository, but it cannot handle setting up the repository initially, so we'll do that now. Log in to your server:&lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;ssh &lt;span class="highlight"&gt;sammy&lt;/span&gt;@&lt;span class="highlight"&gt;your_server_ip&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ensure you're in your home directory. Then use Git to clone your repository. Be sure to replace &lt;code&gt;sammy&lt;/code&gt; with your GitHub username and &lt;code&gt;hello_hapi&lt;/code&gt; with the name of your Github project.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cd
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;git clone https://github.com/&lt;span class="highlight"&gt;sammy&lt;/span&gt;/&lt;span class="highlight"&gt;hello_hapi&lt;/span&gt;.git
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will create a new directory containing your project. You'll use this directory in the next step.&lt;/p&gt;

&lt;p&gt;With your project cloned, you can create the webhook script.&lt;/p&gt;

&lt;h2 id="step-3-—-creating-the-webhook-script"&gt;Step 3 — Creating the Webhook Script&lt;/h2&gt;

&lt;p&gt;Let's create our server to listen for those webhook requests from GitHub. We'll write a Node.js script that launches a web server on port &lt;code&gt;8080&lt;/code&gt;. The server will listen for requests from the webhook, verify the secret we specified, and pull the latest version of the code from GitHub.&lt;/p&gt;

&lt;p&gt;Navigate to your home directory:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cd ~
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Create a new directory for your webhook script called &lt;code&gt;NodeWebhooks&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;mkdir ~/NodeWebhooks
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then navigate to the new directory:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cd ~/NodeWebhooks
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Create a new file called &lt;code&gt;webhook.js&lt;/code&gt; inside of the &lt;code&gt;NodeWebhooks&lt;/code&gt; directory.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nano webhook.js
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add these two lines to the script:&lt;/p&gt;
&lt;div class="code-label " title="webhook.js"&gt;webhook.js&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-js"&gt;var secret = "&lt;span class="highlight"&gt;your_secret_here&lt;/span&gt;";
var repo = "&lt;span class="highlight"&gt;/home/sammy/hello_hapi&lt;/span&gt;";
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first line defines a variable to hold the secret you created in Step 1 which verifies that requests come from GitHub. The second line defines a variable that holds the full path to the repository you want to update on your local disk. This should point to the repository you checked out in Step 2.&lt;/p&gt;

&lt;p&gt;Next, add these lines which import the &lt;code&gt;http&lt;/code&gt; and &lt;code&gt;crypto&lt;/code&gt; libaries into the script. We'll use these to create our web server and hash the secret so we can compare it with what we receive from GitHub:&lt;/p&gt;
&lt;div class="code-label " title="webhook.js"&gt;webhook.js&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-js"&gt;let http = require('http');
let crypto = require('crypto');
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, include the &lt;code&gt;child_process&lt;/code&gt; library so you can execute shell commands from your script:&lt;/p&gt;
&lt;div class="code-label " title="webhook.js"&gt;webhook.js&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-js"&gt;const exec = require('child_process').exec;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, add this code to define a new web server that handles GitHub webhook requests and pulls down the new version of the code if it's an authentic request:&lt;/p&gt;
&lt;div class="code-label " title="webhook.js"&gt;webhook.js&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-js"&gt;http.createServer(function (req, res) {
    req.on('data', function(chunk) {
        let sig = "sha1=" + crypto.createHmac('sha1', secret).update(chunk.toString()).digest('hex');

        if (req.headers['x-hub-signature'] == sig) {
            exec('cd ' + repo + ' &amp;amp;&amp;amp; git pull');
        }
    });

    res.end();
}).listen(8080);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;http.createServer()&lt;/code&gt; function starts a web server on port &lt;code&gt;8080&lt;/code&gt; which listens for incoming requests from Github. For security purposes, we validate that the secret included in the request matches the one we specified when creating the webhook in Step 1. The secret is passed in the &lt;code&gt;x-hub-signature&lt;/code&gt; header as an SHA1-hashed string, so we hash our secret and compare it to what GitHub sends us.&lt;/p&gt;

&lt;p&gt;If the request is authentic, we execute a shell command to update our local repository using &lt;code&gt;git pull&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The completed script looks like this:&lt;/p&gt;
&lt;div class="code-label " title="webhook.js"&gt;webhook.js&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-js"&gt;const secret = "&lt;span class="highlight"&gt;your_secret_here&lt;/span&gt;";
const repo = "&lt;span class="highlight"&gt;~/your_repo_path_here/&lt;/span&gt;";

const http = require('http');
const crypto = require('crypto');
const exec = require('child_process').exec;

http.createServer(function (req, res) {
    req.on('data', function(chunk) {
        let sig = "sha1=" + crypto.createHmac('sha1', secret).update(chunk.toString()).digest('hex');

        if (req.headers['x-hub-signature'] == sig) {
            exec('cd ' + repo + ' &amp;amp;&amp;amp; git pull');
        }
    });

    res.end();
}).listen(8080);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you followed the initial server setup guide, you will need to allow this web server to communicate with the outside web by allowing traffic on port &lt;code&gt;8080&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw allow 8080/tcp
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that our script is in place, let's make sure that it is working properly.&lt;/p&gt;

&lt;h2 id="step-4-testing-the-webhook"&gt;Step 4 - Testing the Webhook&lt;/h2&gt;

&lt;p&gt;We can test our webhook by using &lt;code&gt;node&lt;/code&gt; to run it in the command line. Start the script and leave the process open in your terminal:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cd ~/NodeWebhooks
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;nodejs webhook.js
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Return to your project's page on &lt;a href="https://github.com"&gt;Github.com&lt;/a&gt;. Click on the &lt;strong&gt;Settings&lt;/strong&gt; tab in the top menu bar on your repository's page, followed by clicking &lt;strong&gt;Webhooks&lt;/strong&gt; in the left navigation menu. Click &lt;strong&gt;Edit&lt;/strong&gt; next to the webhook you set up in Step 1. Scroll down until you see the &lt;strong&gt;Recent Deliveries&lt;/strong&gt; section, as shown in the following image:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/node_webhook_sync_1604/oniwqcg.png" alt="Edit Webhook"&gt;&lt;/p&gt;

&lt;p&gt;Press the three dots to the far right to reveal the &lt;strong&gt;Redeliver&lt;/strong&gt; button. With the node server running,  click &lt;strong&gt;Redeliver&lt;/strong&gt; to send the request again. Once you confirm you want to send the request, you'll see a successful response. This is indicated by a &lt;code&gt;200 OK&lt;/code&gt; response code after redelivering the ping.&lt;/p&gt;

&lt;p&gt;We can now move on to making sure our script runs in the background and starts at boot. Use &lt;code&gt;CTRL+C&lt;/code&gt; stops the node webhook server. &lt;/p&gt;

&lt;h2 id="step-5-—-installing-the-webhook-as-a-systemd-service"&gt;Step 5 — Installing the Webhook as a Systemd Service&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.digitalocean.com/community/tutorials/systemd-essentials-working-with-services-units-and-the-journal"&gt;systemd&lt;/a&gt; is the task manager Ubuntu uses to control services.  We will set up a service that will allow us to start our webhook script at boot and use systemd commands to manage it like we would with any other service.&lt;/p&gt;

&lt;p&gt;Start by creating a new service file:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/systemd/system/webhook.service
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add the following configuration to the service file which tells systemd how to run the script.  This tells Systemd where to find our node script and describes our service. &lt;/p&gt;

&lt;p&gt;Make sure to replace &lt;code&gt;sammy&lt;/code&gt; with your username.&lt;/p&gt;
&lt;div class="code-label " title="/etc/systemd/system/webhook.service"&gt;/etc/systemd/system/webhook.service&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;[Unit]
Description=Github webhook
After=network.target

[Service]
Environment=NODE_PORT=&lt;span class="highlight"&gt;8080&lt;/span&gt;
Type=simple
User=&lt;span class="highlight"&gt;sammy&lt;/span&gt;
ExecStart=/usr/bin/nodejs /home/&lt;span class="highlight"&gt;sammy&lt;/span&gt;/NodeWebhooks/webhook.js
Restart=on-failure

[Install]
WantedBy=multi-user.target
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Enable the new service so it starts when the system boots:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl enable webhook.service
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now start the service: &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl start webhook
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ensure the service is started:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl status webhook
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You'll see the following output indicating that the service is active:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;● webhook.service - Github webhook
   Loaded: loaded (/etc/systemd/system/webhook.service; enabled; vendor preset: enabled)
   Active: &lt;span class="highlight"&gt;active&lt;/span&gt; (running) since Fri 2018-08-17 19:28:41 UTC; 6s ago
 Main PID: 9912 (nodejs)
    Tasks: 6
   Memory: 7.6M
      CPU: 95ms
   CGroup: /system.slice/webhook.service
           └─9912 /usr/bin/nodejs /home/sammy/NodeWebhooks/webhook.js

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You are now able to push new commits to your repository and see the changes on your server. &lt;/p&gt;

&lt;p&gt;From your desktop machine, clone the repository:&lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;git clone https://github.com/&lt;span class="highlight"&gt;sammy&lt;/span&gt;/&lt;span class="highlight"&gt;hello_hapi&lt;/span&gt;.git
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Make a change to one of the files in the repository. Then commit the file and push your code to GitHub.&lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;git add &lt;span class="highlight"&gt;index.js&lt;/span&gt;
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;git commit -m "&lt;span class="highlight"&gt;Update index file&lt;/span&gt;"
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;git push origin master
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The webhook will fire and your changes will appear on your server.&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;You have set up a Node.js script which will automatically deploy new commits to a remote repository. You can use this process to set up additional repositories that you'd like to monitor. You could even configure it to deploy a website or application to production when you push your repository.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-generate-short-unique-digital-address-any-location-angularjs-php</id>
    <published>2018-08-03T20:33:22Z</published>
    <updated>2018-08-17T21:22:59Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-generate-short-unique-digital-address-any-location-angularjs-php"/>
    <title>How to Generate a Short and Unique Digital Address for Any Location Using AngularJS and PHP</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;Postal addresses are usually lengthy and sometimes difficult to remember. There are a number of scenarios where a shorter address would be desirable. For example, having the ability to send a short address consisting of only a couple of characters could ensure faster delivery of emergency ambulance services. Pieter Geelen and Harold Goddijn developed the &lt;a href="http://www.mapcode.com/"&gt;Mapcode system&lt;/a&gt; in 2001 to make it easy to create a short-form address for any physical address in the world.&lt;/p&gt;

&lt;p&gt;In this tutorial, you will develop a web app that uses the Google Maps API to generate a short digital address for any address of your choice. You will do this by cloning the base code for this app from GitHub and then adding code to it that will make it fully functional. This app will also be able to retrieve the original physical address from a given mapcode.&lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;In order to complete this tutorial, you will need the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Access to an Ubuntu 18.04 server. This server should have a non-&lt;strong&gt;root&lt;/strong&gt; user with &lt;code&gt;sudo&lt;/code&gt; privileges and a firewall configured. To set this up, you can follow our &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-18-04"&gt;Initial Server Setup Guide for Ubuntu 18.04&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A LAMP stack installed on your machine. This is necessary because the application that you are going to develop in this tutorial uses AngularJS and PHP, and the digital address that the application generates will be stored in a MySQL database. Follow our guide on &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-linux-apache-mysql-php-lamp-stack-ubuntu-18-04"&gt;How To Install Linux, Apache, MySQL, PHP (LAMP) stack on Ubuntu 18.04&lt;/a&gt; to set this up.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Git installed on your server. You can follow the tutorial &lt;a href="https://www.digitalocean.com/community/tutorials/contributing-to-open-source-getting-started-with-git"&gt;Contributing to Open Source: Getting Started with Git&lt;/a&gt; to install and set up Git.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="step-1-—-getting-a-google-api-key"&gt;Step 1 — Getting a Google API Key&lt;/h2&gt;

&lt;p&gt;In this tutorial, you will use JavaScript to create an interface to Google Maps. Google assigns API keys to enable developers to use the JavaScript API on Google Maps, which you will need to obtain and add to your web app's code.&lt;/p&gt;

&lt;p&gt;To get your own API key, head to Google's &lt;a href="https://developers.google.com/maps/documentation/javascript/get-api-key"&gt;"Get API Key" page&lt;/a&gt;. Click on the &lt;strong&gt;GET STARTED&lt;/strong&gt; button in Step 1, and a pop-up will open as shown in the following image:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/mapcode/googlemapsapi1.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Select &lt;strong&gt;Maps&lt;/strong&gt; by clicking the check box and hit &lt;strong&gt;CONTINUE&lt;/strong&gt;. If you aren't already logged into a Google account, you will be asked to do so. Then, the window will ask you to provide a name for the project, which can be anything you'd like:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/mapcode/googlemapsapi2.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Following this, it will ask you to enter your billing information. Note that Google provides API keys as part of a free trial, but it requires you to set up and enable billing in order retrieve them.&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/mapcode/googlemapsapi3.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;After entering this information, your API key will appear on the screen. Copy and store it in a location where you can easily retrieve it, as you will need to add it to your project code later on.&lt;/p&gt;

&lt;p&gt;After obtaining your API key, you can begin building the foundation of your application by creating a MySQL database.&lt;/p&gt;

&lt;h2 id="step-2-—-creating-the-database"&gt;Step 2 — Creating the Database&lt;/h2&gt;

&lt;p&gt;The web application described in this tutorial accepts an address from the user and generates a mapcode for it along with the latitude and longitude of the specified location. You will store this data in a MySQL database so that you can retrieve it later on just by entering the respective digital address.&lt;/p&gt;

&lt;p&gt;Begin by opening the MySQL shell and authenticating with your password:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;mysql -u root -p
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At the prompt, create a database called &lt;code&gt;digitaladdress&lt;/code&gt; using the following command:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;CREATE DATABASE IF NOT EXISTS `digitaladdress`;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, select this new database so that you can create a table within it:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;USE `digitaladdress`;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After selecting the &lt;code&gt;digitaladdress&lt;/code&gt; database, create a table called &lt;code&gt;locations&lt;/code&gt; within it to store the physical address, its longitude, latitude, and the mapcode that your application will create from this data. Run the following &lt;code&gt;CREATE TABLE&lt;/code&gt; statement to create the &lt;code&gt;locations&lt;/code&gt; table within the database:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;CREATE TABLE `locations` (
&lt;/li&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;  `digitaladdress` varchar(50) DEFAULT NULL,
&lt;/li&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;  `state` varchar(30) DEFAULT NULL,
&lt;/li&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;  `zip` varchar(30) DEFAULT NULL,
&lt;/li&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;  `street` varchar(30) DEFAULT NULL,
&lt;/li&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;  `town` varchar(30) DEFAULT NULL,
&lt;/li&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;  `house` varchar(30) DEFAULT NULL,
&lt;/li&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;  `latitude` varchar(30) DEFAULT NULL,
&lt;/li&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;  `longitude` varchar(30) DEFAULT NULL,
&lt;/li&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;  KEY `digitaladdress` (`digitaladdress`)
&lt;/li&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;);
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This table has eight columns: &lt;code&gt;digitaladdress&lt;/code&gt;, &lt;code&gt;state&lt;/code&gt;, &lt;code&gt;zip&lt;/code&gt;, &lt;code&gt;street&lt;/code&gt;, &lt;code&gt;town&lt;/code&gt;, &lt;code&gt;house&lt;/code&gt;, &lt;code&gt;latitude&lt;/code&gt;, and &lt;code&gt;longitude&lt;/code&gt;. The first column, &lt;code&gt;digitaladdress&lt;/code&gt;, is &lt;em&gt;indexed&lt;/em&gt; using the &lt;code&gt;KEY&lt;/code&gt; command. Indexes in MySQL function similarly to how they work in an encyclopedia or other reference work. Any time you or your application issue a query containing a &lt;code&gt;WHERE&lt;/code&gt; statement, MySQL reads every entry in each column, row-by-row, which can become an extremely resource-intensive process as your table accumulates more and more entries. Indexing a column like this takes the data from the column and stores it alphabetically in a separate location, which means that MySQL will not have to look through every row in the table. It only has to find the data you're looking for in the index and then jump to the corresponding row in the table.&lt;/p&gt;

&lt;p&gt;After adding this table, exit the MySQL prompt:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;exit
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With your database and table set up and your Google Maps API key in hand, you're ready to create the project itself.&lt;/p&gt;

&lt;h2 id="step-3-—-creating-the-project"&gt;Step 3 — Creating the Project&lt;/h2&gt;

&lt;p&gt;As mentioned in the introduction, we will clone the base code for this project from GitHub and then add some extra code to make the application functional. The reason for this, rather than walking you through the process of creating each file and adding all the code yourself, is to speed up the process of getting the app running. It will also allow us to focus on adding and understanding the code that allows the app to communicate with both the Google Maps and Mapcode APIs.&lt;/p&gt;

&lt;p&gt;You can find the skeleton code for the full project on &lt;a href="https://github.com/do-community/digiaddress"&gt;this GitHub project page&lt;/a&gt;. Use the following &lt;code&gt;git&lt;/code&gt; command to clone the project to your server:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;git clone https://github.com/do-community/digiaddress.git
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will create a new folder called &lt;code&gt;digiaddress&lt;/code&gt; in your home directory. Move this directory to your server's web root. If you followed the LAMP stack tutorial linked in the prerequisites, this will be the &lt;code&gt;/var/www/html&lt;/code&gt; directory:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo mv digiaddress/ /var/www/html/
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This project contains several PHP and JS files to which you'll add some code later on in this tutorial. To view the directory structure, first install the &lt;code&gt;tree&lt;/code&gt; package using &lt;code&gt;apt&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install tree
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then run the &lt;code&gt;tree&lt;/code&gt; command with the &lt;code&gt;digiaddress&lt;/code&gt; directory given as an argument:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;tree /var/www/html/digiaddress/
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;digiaddress/
├── README.md
├── db.php
├── fetchaddress.php
├── findaddress.php
├── generateDigitalAddress.php
├── geoimplement.php
├── index.php
└── js
    ├── createDigitialAddressApp.js
    └── findAddressApp.js
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can see from this output that the project consists of six PHP files and two JavaScript files. Together, these files create the application's two main functionalities: creating a mapcode from a physical address, and decoding a mapcode to retrieve the original physical address. The following files enable the first functionality:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;index.php&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;geoimplement.php&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;generateDigitialAddress.php&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;db.php&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;createDigitialAddressApp.js&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;index.php&lt;/code&gt; file contains the code for the application's user interface (UI), which consists of a form where users can enter a physical address. The &lt;code&gt;index.php&lt;/code&gt; file calls the &lt;code&gt;geoimplement.php&lt;/code&gt; file any time a user submits the form. &lt;code&gt;geoimplement.php&lt;/code&gt; makes a call to the Google Maps API and passes the address along to it. The Google server then responds with a JSON containing the specified address's information, including its latitude and longitude. This information is then passed to the &lt;code&gt;generateDigitalAddress.php&lt;/code&gt; file which calls the Mapcode API to obtain a mapcode for the given location, as specified by its latitude and longitude. The resulting mapcode, along with the latitude, longitude, and the physical address, are then stored in the database that you created in Step 2. &lt;code&gt;db.php&lt;/code&gt; acts as a helper for this operation. The &lt;code&gt;createDigitalAddressApp.js&lt;/code&gt; file performs a number of operations that control the UX elements seen in the app, including setting a marker and boundary rectangle on the Google Maps interface.&lt;/p&gt;

&lt;p&gt;The remaining three files enable the second function of the application — that is, retrieving a physical address from a given mapcode:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;findaddress.php&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fetchaddress.php&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;findAddressApp.js&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;findaddress.php&lt;/code&gt; file defines the application UI, which is distinct from the one defined in &lt;code&gt;index.php&lt;/code&gt;. The application accepts a previously-generated mapcode as an input and displays the corresponding physical address stored in the database. Whenever a user submits this form, &lt;code&gt;findaddress.php&lt;/code&gt; sends a call to &lt;code&gt;fetchaddress.php&lt;/code&gt; which then retrieves the respective mapcode from the database. The &lt;code&gt;findAddressApp.js&lt;/code&gt; file contains the helper code for setting a marker and a boundary rectangle on the Google Maps interface.&lt;/p&gt;

&lt;p&gt;Test the installation by visiting &lt;code&gt;http://&lt;span class="highlight"&gt;your_server_ip&lt;/span&gt;/digiaddress&lt;/code&gt; in your browser, making sure to change &lt;code&gt;&lt;span class="highlight"&gt;your_server_ip&lt;/span&gt;&lt;/code&gt; to reflect your server's IP address.&lt;/p&gt;

&lt;span class='note'&gt;&lt;p&gt;
&lt;strong&gt;Note:&lt;/strong&gt; If you don't know your server's IP address, you can run the following &lt;code&gt;curl&lt;/code&gt; command. This command will print the page content of &lt;code&gt;icanhazip.com&lt;/code&gt;, a website that shows the IP address of the machine accessing it:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;curl http://icanhazip.com
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;/p&gt;&lt;/span&gt;

&lt;p&gt;Once there, you will see this heading at the top of your browser window:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;Generate Digital Address
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This confirms that you have correctly downloaded the project files. With that, let us proceed with the development of the app's primary function: generating a mapcode.&lt;/p&gt;

&lt;h2 id="step-4-—-developing-the-application-39-s-ui"&gt;Step 4 — Developing the Application's UI&lt;/h2&gt;

&lt;p&gt;While the boilerplate code for the application interface is included in the files you downloaded in the previous step, you still need to make a few changes and additions to some of these files to make the application functional and engaging for users. We will get started with updating the code to develop the application's UI.&lt;/p&gt;

&lt;p&gt;Open the &lt;code&gt;index.php&lt;/code&gt; file using your preferred editor. Here, we'll use &lt;code&gt;nano&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nano /var/www/html/digiaddress/index.php
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Look for the following line of code:&lt;/p&gt;
&lt;div class="code-label " title="/var/www/html/digiaddress/index.php"&gt;/var/www/html/digiaddress/index.php&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .
&amp;lt;script async defer src="https://maps.googleapis.com/maps/api/js?key=&lt;span class="highlight"&gt;&amp;lt;YOUR KEY&amp;gt;&lt;/span&gt;"&amp;gt;&amp;lt;/script&amp;gt;
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Replace &lt;code&gt;&lt;span class="highlight"&gt;&amp;lt;YOUR KEY&amp;gt;&lt;/span&gt;&lt;/code&gt; with the Google API key you obtained in Step 1. After adding your API key, the line should look similar to this:&lt;/p&gt;
&lt;div class="code-label " title="/var/www/html/digiaddress/index.php"&gt;/var/www/html/digiaddress/index.php&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .
&amp;lt;script async defer src="https://maps.googleapis.com/maps/api/js?key=&lt;span class="highlight"&gt;ExampleAPIKeyH2vITfv1eIHbfka9ym634Esw7u&lt;/span&gt;"&amp;gt;&amp;lt;/script&amp;gt;
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, find the following comment in the &lt;code&gt;index.php&lt;/code&gt; file:&lt;/p&gt;
&lt;div class="code-label " title="/var/www/html/digiaddress/index.php"&gt;/var/www/html/digiaddress/index.php&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .
            &amp;lt;!-- add form code here --&amp;gt;
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We'll add a few dozen lines of code below this comment which will create a form where users can enter the address of a physical location which the application will use to generate a mapcode. Under this comment, add the following highlighted code which creates a title called &lt;strong&gt;Enter Address&lt;/strong&gt; at the top of the form:&lt;/p&gt;
&lt;div class="code-label " title="/var/www/html/digiaddress/index.php"&gt;/var/www/html/digiaddress/index.php&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .
            &amp;lt;!-- add form code here --&amp;gt;

            &lt;span class="highlight"&gt;&amp;lt;div class="form-border spacing-top"&amp;gt;&lt;/span&gt;
                &lt;span class="highlight"&gt;&amp;lt;div class="card-header" style="background:#cc0001; color:#ffff"&amp;gt;&lt;/span&gt;
                    &lt;span class="highlight"&gt;&amp;lt;h5&amp;gt;Enter Address&amp;lt;/h5&amp;gt;&lt;/span&gt;
                &lt;span class="highlight"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                &lt;span class="highlight"&gt;&amp;lt;div class="extra-padding"&amp;gt;&lt;/span&gt;
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Below this, add the following HTML code. This creates a form with five text fields (along with their appropriate labels) where users will input their information:&lt;/p&gt;
&lt;div class="code-label " title="/var/www/html/digiaddress/index.php"&gt;/var/www/html/digiaddress/index.php&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;                . . .
                &lt;span class="highlight"&gt;&amp;lt;form&amp;gt;&lt;/span&gt;
                        &lt;span class="highlight"&gt;&amp;lt;div class="form-group input-group-sm"&amp;gt;&lt;/span&gt;
                            &lt;span class="highlight"&gt;&amp;lt;label for="state"&amp;gt;State&amp;lt;/label&amp;gt;&lt;/span&gt;
                            &lt;span class="highlight"&gt;&amp;lt;input type="text" class="form-control rounded-0 textbox-border" id="state"&lt;/span&gt;
                                   &lt;span class="highlight"&gt;placeholder="" ng-model="address.state"/&amp;gt;&lt;/span&gt;
                        &lt;span class="highlight"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                        &lt;span class="highlight"&gt;&amp;lt;div class="form-group input-group-sm"&amp;gt;&lt;/span&gt;
                            &lt;span class="highlight"&gt;&amp;lt;label for="zip" class="animated-label"&amp;gt;Zip&amp;lt;/label&amp;gt;&lt;/span&gt;
                            &lt;span class="highlight"&gt;&amp;lt;input type="text" class="form-control rounded-0 textbox-depth textbox-border"&lt;/span&gt;
                                   &lt;span class="highlight"&gt;id="zip" ng-model="address.zip" disabled="disabled"/&amp;gt;&lt;/span&gt;
                        &lt;span class="highlight"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                        &lt;span class="highlight"&gt;&amp;lt;div class="form-group input-group-sm"&amp;gt;&lt;/span&gt;
                            &lt;span class="highlight"&gt;&amp;lt;label for="town"&amp;gt;Town&amp;lt;/label&amp;gt;&lt;/span&gt;
                            &lt;span class="highlight"&gt;&amp;lt;input type="text" class="form-control rounded-0 textbox-border"&lt;/span&gt;
                                   &lt;span class="highlight"&gt;id="town" ng-model="address.town" disabled="disabled"/&amp;gt;&lt;/span&gt;
                        &lt;span class="highlight"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                        &lt;span class="highlight"&gt;&amp;lt;div class="form-group input-group-sm"&amp;gt;&lt;/span&gt;
                            &lt;span class="highlight"&gt;&amp;lt;label for="street"&amp;gt;Street&amp;lt;/label&amp;gt;&lt;/span&gt;
                            &lt;span class="highlight"&gt;&amp;lt;input type="text" class="form-control rounded-0 textbox-border" id="street"&lt;/span&gt;
                                   &lt;span class="highlight"&gt;placeholder="" ng-model="address.street" disabled="disabled"/&amp;gt;&lt;/span&gt;
                        &lt;span class="highlight"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                        &lt;span class="highlight"&gt;&amp;lt;div class="form-group input-group-sm"&amp;gt;&lt;/span&gt;
                            &lt;span class="highlight"&gt;&amp;lt;label for="house"&amp;gt;House&amp;lt;/label&amp;gt;&lt;/span&gt;
                            &lt;span class="highlight"&gt;&amp;lt;input type="text" class="form-control rounded-0 textbox-border" id="house"&lt;/span&gt;
                                   &lt;span class="highlight"&gt;placeholder="" ng-model="address.house" disabled="disabled"/&amp;gt;&lt;/span&gt;
                        &lt;span class="highlight"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                 . . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Below the form code, add the following lines. These create two hidden controls which pass along the latitude and longitude information derived from any address submitted through the form:&lt;/p&gt;
&lt;div class="code-label " title="/var/www/html/digiaddress/index.php"&gt;/var/www/html/digiaddress/index.php&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;                            . . .
                            &lt;span class="highlight"&gt;&amp;lt;div class="form-group input-group-sm"&amp;gt;&lt;/span&gt;
                                &lt;span class="highlight"&gt;&amp;lt;input type="hidden" ng-model="address.lat"/&amp;gt;&lt;/span&gt;
                            &lt;span class="highlight"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                            &lt;span class="highlight"&gt;&amp;lt;div class="form-group input-group-sm"&amp;gt;&lt;/span&gt;
                                &lt;span class="highlight"&gt;&amp;lt;input type="hidden" ng-model="address.long"/&amp;gt;&lt;/span&gt;
                            &lt;span class="highlight"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                            . . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Lastly, close out this section by adding the following code. This creates a &lt;strong&gt;Generate&lt;/strong&gt; button which will allow users to submit the form:&lt;/p&gt;
&lt;div class="code-label " title="/var/www/html/digiaddress/index.php"&gt;/var/www/html/digiaddress/index.php&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;                            . . .
                            &lt;span class="highlight"&gt;&amp;lt;button type="submit" disabled="disabled" class="btn btn-color btn-block rounded-0" id="generate"&lt;/span&gt;
                                    &lt;span class="highlight"&gt;style="color:#ffff;background-color: #cc0001;"&amp;gt;Generate&lt;/span&gt;
                            &lt;span class="highlight"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
                    &lt;span class="highlight"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
                &lt;span class="highlight"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
            &lt;span class="highlight"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
        . . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After adding these elements, this section of the file should match this:&lt;/p&gt;
&lt;div class="code-label " title="/var/www/html/digiaddress/index.php"&gt;/var/www/html/digiaddress/index.php&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .
            &amp;lt;!-- add form code here --&amp;gt;

            &amp;lt;div class="form-border spacing-top"&amp;gt;
                &amp;lt;div class="card-header" style="background:#cc0001; color:#ffff"&amp;gt;
                    &amp;lt;h5&amp;gt;Enter Address&amp;lt;/h5&amp;gt;
                &amp;lt;/div&amp;gt;
                &amp;lt;div class="extra-padding"&amp;gt;
                    &amp;lt;form&amp;gt;    
                            &amp;lt;div class="form-group input-group-sm"&amp;gt;
                                &amp;lt;label for="state"&amp;gt;State&amp;lt;/label&amp;gt;
                                &amp;lt;input type="text" class="form-control rounded-0 textbox-border" id="state"
                                       placeholder="" ng-model="address.state"/&amp;gt;
                            &amp;lt;/div&amp;gt;
                            &amp;lt;div class="form-group input-group-sm"&amp;gt;
                                &amp;lt;label for="zip" class="animated-label"&amp;gt;Zip&amp;lt;/label&amp;gt;
                                &amp;lt;input type="text" class="form-control rounded-0 textbox-depth textbox-border"
                                       id="zip" ng-model="address.zip" disabled="disabled"/&amp;gt;
                            &amp;lt;/div&amp;gt;
                            &amp;lt;div class="form-group input-group-sm"&amp;gt;
                                &amp;lt;label for="town"&amp;gt;Town&amp;lt;/label&amp;gt;
                                &amp;lt;input type="text" class="form-control rounded-0 textbox-border "
                                       id="town" ng-model="address.town" disabled="disabled"/&amp;gt;
                            &amp;lt;/div&amp;gt;
                            &amp;lt;div class="form-group input-group-sm"&amp;gt;
                                &amp;lt;label for="street"&amp;gt;Street&amp;lt;/label&amp;gt;
                                &amp;lt;input type="text" class="form-control rounded-0 textbox-border" id="street"
                                       placeholder="" ng-model="address.street" disabled="disabled"/&amp;gt;
                            &amp;lt;/div&amp;gt;
                            &amp;lt;div class="form-group input-group-sm"&amp;gt;
                                &amp;lt;label for="house"&amp;gt;House&amp;lt;/label&amp;gt;
                                &amp;lt;input type="text" class="form-control rounded-0 textbox-border" id="house"
                                       placeholder="" ng-model="address.house" disabled="disabled"/&amp;gt;
                            &amp;lt;/div&amp;gt;

                            &amp;lt;div class="form-group input-group-sm"&amp;gt;
                                &amp;lt;input type="hidden" ng-model="address.lat"/&amp;gt;
                            &amp;lt;/div&amp;gt;
                            &amp;lt;div class="form-group input-group-sm"&amp;gt;
                                &amp;lt;input type="hidden" ng-model="address.long"/&amp;gt;
                            &amp;lt;/div&amp;gt;
                            &amp;lt;button type="submit" disabled="disabled" class="btn btn-color btn-block rounded-0" id="generate"
                                    style="color:#ffff;background-color: #cc0001;"&amp;gt;Generate
                            &amp;lt;/button&amp;gt;
                    &amp;lt;/form&amp;gt;
                &amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;
            &amp;lt;br&amp;gt;
        &amp;lt;/div&amp;gt;

        &amp;lt;!-- add google map control --&amp;gt;
                    . . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save the file by pressing &lt;code&gt;CTRL+O&lt;/code&gt; then &lt;code&gt;ENTER&lt;/code&gt;, and then visit the application in your browser again:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;http://&lt;span class="highlight"&gt;your_server_ip&lt;/span&gt;/digiaddress
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will see the newly-added form fields and &lt;strong&gt;Generate&lt;/strong&gt; button, and the application should look like this:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/mapcode/final_form.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;At this point, if you enter address information into the form and try clicking the &lt;strong&gt;Generate&lt;/strong&gt; button, nothing will happen. We will add the mapcode generation functionality later on, but let's first focus on making this page more visually engaging by adding a map which users can interact with.&lt;/p&gt;

&lt;h2 id="step-5-—-adding-google-maps-controls"&gt;Step 5 — Adding Google Maps Controls&lt;/h2&gt;

&lt;p&gt;When maps are displayed on a website through the Google Maps JavaScript API, they contain user interface features that allow visitors to interact with the map they see. These features are known as &lt;em&gt;controls&lt;/em&gt;. We will continue editing the &lt;code&gt;index.php&lt;/code&gt; file to add Google Maps controls to this app and, when finished, users will be able to view a map next to the input form, drag it around to view different locations, zoom in and out, and switch between Google's map, satellite, and street views.&lt;/p&gt;

&lt;p&gt;Find the following comment within the &lt;code&gt;index.php&lt;/code&gt; file:&lt;/p&gt;
&lt;div class="code-label " title="/var/www/html/digiaddress/index.php"&gt;/var/www/html/digiaddress/index.php&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .
&amp;lt;!-- add google map control --&amp;gt;
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add the following highlighted code below this comment:&lt;/p&gt;
&lt;div class="code-label " title="/var/www/html/digiaddress/index.php"&gt;/var/www/html/digiaddress/index.php&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .
        &amp;lt;!-- add google map control --&amp;gt;

        &lt;span class="highlight"&gt;&amp;lt;div class="col-sm-8 map-align" ng-init="initMap()"&amp;gt;&lt;/span&gt;
            &lt;span class="highlight"&gt;&amp;lt;div id="map" class="extra-padding" style="height: 100%;&lt;/span&gt;
            &lt;span class="highlight"&gt;margin-bottom: 15px;"&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
            &lt;span class="highlight"&gt;&amp;lt;label id="geocoordinates" ng-show="latlng" ng-model="lt"&amp;gt;&amp;lt;/label&amp;gt;&amp;lt;br/&amp;gt;&lt;/span&gt;
            &lt;span class="highlight"&gt;&amp;lt;label id="geoaddress" ng-show="address" ng-model="padd"&amp;gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
            &lt;span class="highlight"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
        &lt;span class="highlight"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save the file, then visit the application in your browser again. You will see the following:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/mapcode/final_googlemap.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;As you can see, we've successfully added a map to the application. You can drag the map around to focus on different locations, zoom in and out, and switch between the map, satellite, and street views. Looking back at the code you just added, notice that we've also added two label controls that will display the geocoordinates and the physical address that were entered on the form:&lt;/p&gt;
&lt;div class="code-label " title="/var/www/html/digiaddress/index.php"&gt;/var/www/html/digiaddress/index.php&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;            . . .
            &amp;lt;label id="geocoordinates" ng-show="latlng" ng-model="lt"&amp;gt;&amp;lt;/label&amp;gt;&amp;lt;br/&amp;gt;
            &amp;lt;label id="geoaddress" ng-show="address" ng-model="padd"&amp;gt;&amp;lt;/label&amp;gt;
            . . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Visit the application again in your browser and enter the name of a state in the first field. When you move your text cursor to the next field, the latitude and longitude labels don't appear, nor does the location shown on the map change to reflect the information you've entered. Let's enable these behaviors.&lt;/p&gt;

&lt;h2 id="step-6-—-adding-event-listeners"&gt;Step 6 — Adding Event Listeners&lt;/h2&gt;

&lt;p&gt;Adding interactive elements to an application can help to keep its users engaged. We will implement a few interactive behaviors in this application through the use of &lt;em&gt;event listeners&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;An &lt;em&gt;event&lt;/em&gt; is any action that takes place on a web page. Events can be something done by a user or by the browser itself. Examples of common events are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clicking an HTML button&lt;/li&gt;
&lt;li&gt;Changing the content of an input field&lt;/li&gt;
&lt;li&gt;Changing the focus from one page element to another&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An &lt;em&gt;event listener&lt;/em&gt; is a directive that tells a program to take a certain action when a specific event takes place. In AngularJS, event listeners are defined with directives that generally follow this format:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;ng-&lt;span class="highlight"&gt;event_type&lt;/span&gt;=&lt;span class="highlight"&gt;expression&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this step, we will add an event listener that helps to process the information entered by users into a mapcode whenever they submit the form. We will also add a couple more event listeners that will make the application more interactive. Specifically, we'll use these listeners to change the location shown in the application map, place a marker, and draw a rectangle around the location as users enter information into the form. We'll add these event listeners to &lt;code&gt;index.php&lt;/code&gt;, so open that file up again if you've closed it:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nano /var/www/html/digiaddress/index.php
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Scroll down to the first batch of code we added, and find the block that begins with &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt;. It will look like this:&lt;/p&gt;
&lt;div class="code-label " title="/var/www/html/digiaddress/index.php"&gt;/var/www/html/digiaddress/index.php&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;                . . .
                    &amp;lt;form&amp;gt;
                            &amp;lt;div class="form-group input-group-sm"&amp;gt;
                                &amp;lt;label for="state"&amp;gt;State&amp;lt;/label&amp;gt;
                                &amp;lt;input type="text" class="form-control rounded-0 textbox-border" id="state"
                                       placeholder="" ng-model="address.state"/&amp;gt;
                            &amp;lt;/div&amp;gt;
                            &amp;lt;div class="form-group input-group-sm"&amp;gt;
                                &amp;lt;label for="zip" class="animated-label"&amp;gt;Zip&amp;lt;/label&amp;gt;
                                &amp;lt;input type="text" class="form-control rounded-0 textbox-depth textbox-border"
                                       id="zip" ng-model="address.zip" disabled="disabled"/&amp;gt;
                            &amp;lt;/div&amp;gt;
                            &amp;lt;div class="form-group input-group-sm"&amp;gt;
                                &amp;lt;label for="town"&amp;gt;Town&amp;lt;/label&amp;gt;
                                &amp;lt;input type="text" class="form-control rounded-0 textbox-border"
                                       id="town" ng-model="address.town" disabled="disabled"/&amp;gt;
                            &amp;lt;/div&amp;gt;
                            &amp;lt;div class="form-group input-group-sm"&amp;gt;
                                &amp;lt;label for="street"&amp;gt;Street&amp;lt;/label&amp;gt;
                                &amp;lt;input type="text" class="form-control rounded-0 textbox-border" id="street"
                                       placeholder="" ng-model="address.street" disabled="disabled"/&amp;gt;
                            &amp;lt;/div&amp;gt;
                            &amp;lt;div class="form-group input-group-sm"&amp;gt;
                                &amp;lt;label for="house"&amp;gt;House&amp;lt;/label&amp;gt;
                                &amp;lt;input type="text" class="form-control rounded-0 textbox-border" id="house"
                                       placeholder="" ng-model="address.house" disabled="disabled"/&amp;gt;
                            &amp;lt;/div&amp;gt;
                    &amp;lt;/form&amp;gt;
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To begin, add the following highlighted event listener to the opening &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; tag. This code tells the app to call the &lt;code&gt;processForm&lt;/code&gt; function whenever a user submits information through the form. &lt;code&gt;processForm&lt;/code&gt; is defined in the &lt;code&gt;createDigitalAddressApp.js&lt;/code&gt; file, and serves as a helper function that sends the information submitted by users to the appropriate files which then process it into a mapcode. We will take a closer look at this function in Step 7:&lt;/p&gt;
&lt;div class="code-label " title="/var/www/html/digiaddress/index.php"&gt;/var/www/html/digiaddress/index.php&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;                . . .
                    &amp;lt;form &lt;span class="highlight"&gt;ng-submit="processForm()" class="custom-form"&lt;/span&gt;&amp;gt;
                            &amp;lt;div class="form-group input-group-sm"&amp;gt;
                                &amp;lt;label for="state"&amp;gt;State&amp;lt;/label&amp;gt;
                                &amp;lt;input type="text" class="form-control rounded-0 textbox-border" id="state"
                                       placeholder="" ng-model="address.state"
                            &amp;lt;/div&amp;gt;
                . . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, continue editing this block by adding a couple &lt;code&gt;blur&lt;/code&gt; event listeners. A &lt;code&gt;blur&lt;/code&gt; event occurs when a given page element loses focus. Add the following highlighted lines to the &lt;code&gt;form&lt;/code&gt; block's &lt;code&gt;input&lt;/code&gt; tags. These lines tell the application to call the &lt;code&gt;geocodeAddress&lt;/code&gt; function when a user's focus shifts away from the respective form fields we created in Step 4. Note that you must also delete the slashes and greater-than signs (&lt;code&gt;/&amp;gt;&lt;/code&gt;) that close out each &lt;code&gt;input&lt;/code&gt; tag. Failing to do so will prevent the app from registering the &lt;code&gt;blur&lt;/code&gt; events correctly:&lt;/p&gt;
&lt;div class="code-label " title="/var/www/html/digiaddress/index.php"&gt;/var/www/html/digiaddress/index.php&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;                . . .
                &amp;lt;form ng-submit="processForm()" class="custom-form"&amp;gt;
                            &amp;lt;div class="form-group input-group-sm"&amp;gt;
                                &amp;lt;label for="state"&amp;gt;State&amp;lt;/label&amp;gt;
                                &amp;lt;input type="text" class="form-control rounded-0 textbox-border" id="state"
                                       placeholder="" ng-model="address.state"
                                       &lt;span class="highlight"&gt;ng-blur="geocodeAddress(address,'state')" required=""/&amp;gt;&lt;/span&gt;
                            &amp;lt;/div&amp;gt;
                            &amp;lt;div class="form-group input-group-sm"&amp;gt;
                                &amp;lt;label for="zip" class="animated-label"&amp;gt;Zip&amp;lt;/label&amp;gt;
                                &amp;lt;input type="text" class="form-control rounded-0 textbox-depth textbox-border"
                                       id="zip" ng-model="address.zip" disabled="disabled"
                                       &lt;span class="highlight"&gt;ng-blur="geocodeAddress(address,'zip')" required=""/&amp;gt;&lt;/span&gt;
                            &amp;lt;/div&amp;gt;
                            &amp;lt;div class="form-group input-group-sm"&amp;gt;
                                &amp;lt;label for="town"&amp;gt;Town&amp;lt;/label&amp;gt;
                                &amp;lt;input type="text" class="form-control rounded-0 textbox-border"
                                       id="town" ng-model="address.town" disabled="disabled"
                                       &lt;span class="highlight"&gt;ng-blur="geocodeAddress(address,'town')" required=""/&amp;gt;&lt;/span&gt;
                            &amp;lt;/div&amp;gt;
                            &amp;lt;div class="form-group input-group-sm"&amp;gt;
                                &amp;lt;label for="street"&amp;gt;Street&amp;lt;/label&amp;gt;
                                &amp;lt;input type="text" class="form-control rounded-0 textbox-border" id="street"
                                       placeholder="" ng-model="address.street" disabled="disabled"
                                       &lt;span class="highlight"&gt;ng-blur="geocodeAddress(address,'street')" required=""/&amp;gt;&lt;/span&gt;
                            &amp;lt;/div&amp;gt;
                            &amp;lt;div class="form-group input-group-sm"&amp;gt;
                                &amp;lt;label for="house"&amp;gt;House&amp;lt;/label&amp;gt;
                                &amp;lt;input type="text" class="form-control rounded-0 textbox-border" id="house"
                                       placeholder="" ng-model="address.house" disabled="disabled"
                                       &lt;span class="highlight"&gt;ng-blur="geocodeAddress(address,'house')" required=""/&amp;gt;&lt;/span&gt;
                            &amp;lt;/div&amp;gt;
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first of these new lines — &lt;code&gt;ng-blur="geocodeAddress(address,'state')" required=""/&amp;gt;&lt;/code&gt; — translates to "When the user's focus shifts away from the 'state' field, call the &lt;code&gt;geocodeAddress&lt;/code&gt; function." The other new lines also call &lt;code&gt;geocodeAddress&lt;/code&gt;, albeit when the user's focus shifts away from their respective fields.&lt;/p&gt;

&lt;p&gt;As with the &lt;code&gt;processForm&lt;/code&gt; function, &lt;code&gt;geocodeAddress&lt;/code&gt; is declared in the &lt;code&gt;createDigitalAddressApp.js&lt;/code&gt; file, but there isn't yet any code in that file that defines it. We will complete this function so that it places a marker and draws a rectangle on the application map after these &lt;code&gt;blur&lt;/code&gt; events occur to reflect the information entered into the form. We'll also add some code that takes the address information and processes it into a mapcode.&lt;/p&gt;

&lt;p&gt;Save and close the &lt;code&gt;index.php&lt;/code&gt; file (press &lt;code&gt;CTRL+X&lt;/code&gt;, &lt;code&gt;Y&lt;/code&gt;, then &lt;code&gt;ENTER&lt;/code&gt;) and then open the&lt;code&gt;createDigitalAddressApp.js&lt;/code&gt; file:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nano /var/www/html/digiaddress/js/createDigitalAddressApp.js
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this file, find the following line:&lt;/p&gt;
&lt;div class="code-label " title="/var/www/html/digiaddress/js/createDigitalAddressApp.js"&gt;/var/www/html/digiaddress/js/createDigitalAddressApp.js&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .
$scope.geocodeAddress = function (address, field) {
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This line is where we declare the &lt;code&gt;geocodeAddress&lt;/code&gt; function. A few lines below this, we declare a variable named &lt;code&gt;fullAddress&lt;/code&gt; which constructs a human-readable mailing address from the information entered by a user into the application's form fields. This is done through a series of &lt;code&gt;if&lt;/code&gt; statements:&lt;/p&gt;
&lt;div class="code-label " title="/var/www/html/digiaddress/js/createDigitalAddressApp.js"&gt;/var/www/html/digiaddress/js/createDigitalAddressApp.js&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .
var fullAddress = "";

    if (address ['house']) {
        angular.element(document.getElementById('generate'))[0].disabled = false;
            fullAddress = address ['house'] + ",";
                }
    if (address ['town']) {
        angular.element(document.getElementById('street'))[0].disabled = false;
            fullAddress = fullAddress + address ['town'] + ",";
    }
    if (address ['street']) {
        angular.element(document.getElementById('house'))[0].disabled = false;
            fullAddress = fullAddress + address ['street'] + ",";
    }
    if (address ['state']) {
        angular.element(document.getElementById('zip'))[0].disabled = false;
            fullAddress = fullAddress + address ['state'] + " ";
    }
    if (address ['zip']) {
        angular.element(document.getElementById('town'))[0].disabled = false;
            fullAddress = fullAddress + address ['zip'];
    }
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Directly after these lines is the following comment:&lt;/p&gt;
&lt;div class="code-label " title="/var/www/html/digiaddress/js/createDigitalAddressApp.js"&gt;/var/www/html/digiaddress/js/createDigitalAddressApp.js&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .
// add code for locating the address on Google maps
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Underneath this comment, add the following line which checks whether &lt;code&gt;fullAddress&lt;/code&gt; is any value other than null:&lt;/p&gt;
&lt;div class="code-label " title="/var/www/html/digiaddress/js/createDigitalAddressApp.js"&gt;/var/www/html/digiaddress/js/createDigitalAddressApp.js&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;                . . .
                &lt;span class="highlight"&gt;if (fullAddress !== "") {&lt;/span&gt;
                . . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add the following code below this line. This code submits the information entered into the form to the &lt;code&gt;geoimplement.php&lt;/code&gt; file using the &lt;a href="https://en.wikipedia.org/wiki/POST_(HTTP)"&gt;HTTP &lt;em&gt;POST&lt;/em&gt; method&lt;/a&gt; if &lt;code&gt;fullAddress&lt;/code&gt; is not null:&lt;/p&gt;
&lt;div class="code-label " title="/var/www/html/digiaddress/js/createDigitalAddressApp.js"&gt;/var/www/html/digiaddress/js/createDigitalAddressApp.js&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;                    . . .
                    &lt;span class="highlight"&gt;$http({&lt;/span&gt;
                        &lt;span class="highlight"&gt;method: 'POST',&lt;/span&gt;
                        &lt;span class="highlight"&gt;url: 'geoimplement.php',&lt;/span&gt;
                        &lt;span class="highlight"&gt;data: {address: fullAddress},&lt;/span&gt;
                        &lt;span class="highlight"&gt;headers: {'Content-Type': 'application/x-www-form-urlencoded'}&lt;/span&gt;

                    &lt;span class="highlight"&gt;}).then(function successCallback(results) {&lt;/span&gt;
                    . . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, add the following line which checks whether the PHP call was returned successfully:&lt;/p&gt;
&lt;div class="code-label " title="/var/www/html/digiaddress/js/createDigitalAddressApp.js"&gt;/var/www/html/digiaddress/js/createDigitalAddressApp.js&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;                        . . .
                        &lt;span class="highlight"&gt;if (results.data !== "false") {&lt;/span&gt;
                        . . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If the PHP call was successfully returned, we'll be able to process the result. Add the following line, which removes any boundary rectangle that may have been previously drawn on the map by calling the &lt;code&gt;removeRectangle&lt;/code&gt; function, which is defined at the top of the &lt;code&gt;createDigitalAddressApp.js&lt;/code&gt; file:&lt;/p&gt;
&lt;div class="code-label " title="/var/www/html/digiaddress/js/createDigitalAddressApp.js"&gt;/var/www/html/digiaddress/js/createDigitalAddressApp.js&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;                            . . .
                            &lt;span class="highlight"&gt;removeRectangle();&lt;/span&gt;
                            . . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Under the &lt;code&gt;removeRectangle();&lt;/code&gt; line, add the following four lines which will create a marker pointing to the new location on the map control:&lt;/p&gt;
&lt;div class="code-label " title="/var/www/html/digiaddress/js/createDigitalAddressApp.js"&gt;/var/www/html/digiaddress/js/createDigitalAddressApp.js&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;                            . . .
                            &lt;span class="highlight"&gt;new google.maps.Marker({&lt;/span&gt;
                                &lt;span class="highlight"&gt;map: locationMap,&lt;/span&gt;
                                &lt;span class="highlight"&gt;position: results.data.geometry.location&lt;/span&gt;
                            &lt;span class="highlight"&gt;});&lt;/span&gt;
                            . . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then add the following code, which obtains the latitude and longitude information from the result and displays it with the two HTML labels we created in the &lt;code&gt;index.php&lt;/code&gt; file in Step 5:&lt;/p&gt;
&lt;div class="code-label " title="/var/www/html/digiaddress/js/createDigitalAddressApp.js"&gt;/var/www/html/digiaddress/js/createDigitalAddressApp.js&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;                            . . .
                            &lt;span class="highlight"&gt;lat = results.data.geometry.location.lat;&lt;/span&gt;
                            &lt;span class="highlight"&gt;lng = results.data.geometry.location.lng;&lt;/span&gt;

                            &lt;span class="highlight"&gt;$scope.address.lat = lat;&lt;/span&gt;
                            &lt;span class="highlight"&gt;$scope.address.lng = lng;&lt;/span&gt;

                            &lt;span class="highlight"&gt;geoCoordLabel = angular.element(document.querySelector('#geocoordinates'));&lt;/span&gt;
                            &lt;span class="highlight"&gt;geoCoordLabel.html("Geo Coordinate: " + lat + "," + lng);&lt;/span&gt;

                            &lt;span class="highlight"&gt;geoAddressLabel = angular.element(document.querySelector('#geoaddress'));&lt;/span&gt;
                            &lt;span class="highlight"&gt;geoAddressLabel.html("Geo Address: " + fullAddress);&lt;/span&gt;

                            &lt;span class="highlight"&gt;$scope.latlng = true;&lt;/span&gt;
                            . . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Lastly, below these lines, add the following content. This code creates a viewport which marks a new boundary rectangle on the map:&lt;/p&gt;
&lt;div class="code-label " title="/var/www/html/digiaddress/js/createDigitalAddressApp.js"&gt;/var/www/html/digiaddress/js/createDigitalAddressApp.js&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;                            . . .
                            &lt;span class="highlight"&gt;if (results.data.geometry.viewport) {&lt;/span&gt;

                                &lt;span class="highlight"&gt;rectangle = new google.maps.Rectangle({&lt;/span&gt;
                                    &lt;span class="highlight"&gt;strokeColor: '#FF0000',&lt;/span&gt;
                                    &lt;span class="highlight"&gt;strokeOpacity: 0.8,&lt;/span&gt;
                                    &lt;span class="highlight"&gt;strokeWeight: 0.5,&lt;/span&gt;
                                    &lt;span class="highlight"&gt;fillColor: '#FF0000',&lt;/span&gt;
                                    &lt;span class="highlight"&gt;fillOpacity: 0.35,&lt;/span&gt;
                                    &lt;span class="highlight"&gt;map: locationMap,&lt;/span&gt;
                                    &lt;span class="highlight"&gt;bounds: {&lt;/span&gt;
                                        &lt;span class="highlight"&gt;north: results.data.geometry.viewport.northeast.lat,&lt;/span&gt;
                                        &lt;span class="highlight"&gt;south: results.data.geometry.viewport.southwest.lat,&lt;/span&gt;
                                        &lt;span class="highlight"&gt;east: results.data.geometry.viewport.northeast.lng,&lt;/span&gt;
                                        &lt;span class="highlight"&gt;west: results.data.geometry.viewport.southwest.lng&lt;/span&gt;
                                    &lt;span class="highlight"&gt;}&lt;/span&gt;
                                &lt;span class="highlight"&gt;});&lt;/span&gt;

                                &lt;span class="highlight"&gt;var googleBounds = new google.maps.LatLngBounds(results.data.geometry.viewport.southwest,&lt;/span&gt; &lt;span class="highlight"&gt;results.data.geometry.viewport.northeast);&lt;/span&gt;

                                &lt;span class="highlight"&gt;locationMap.setCenter(new google.maps.LatLng(lat, lng));&lt;/span&gt;
                                &lt;span class="highlight"&gt;locationMap.fitBounds(googleBounds);&lt;/span&gt;
                            &lt;span class="highlight"&gt;}&lt;/span&gt;
                        &lt;span class="highlight"&gt;} else {&lt;/span&gt;
                            &lt;span class="highlight"&gt;errorLabel = angular.element(document.querySelector('#lt'));&lt;/span&gt;
                            &lt;span class="highlight"&gt;errorLabel.html("Place not found.");&lt;/span&gt;
                            &lt;span class="highlight"&gt;$scope.latlng = true;&lt;/span&gt;
                            &lt;span class="highlight"&gt;removeRectangle();&lt;/span&gt;
                        &lt;span class="highlight"&gt;}&lt;/span&gt;

                    &lt;span class="highlight"&gt;}, function errorCallback(results) {&lt;/span&gt;
                       &lt;span class="highlight"&gt;console.log(results);&lt;/span&gt;
                    &lt;span class="highlight"&gt;});&lt;/span&gt;
                &lt;span class="highlight"&gt;}&lt;/span&gt;
                . . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After adding this content, this section of the file will look like this:&lt;/p&gt;
&lt;div class="code-label " title="/var/www/html/digiaddress/js/createDigitalAddressApp.js"&gt;/var/www/html/digiaddress/js/createDigitalAddressApp.js&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;                . . .
                // add code for locating the address on Google maps
                if (fullAddress !== "") {
                    $http({
                        method: 'POST',
                        url: 'geoimplement.php',
                        data: {address: fullAddress},
                        headers: {'Content-Type': 'application/x-www-form-urlencoded'}

                    }).then(function successCallback(results) {

                        if (results.data !== "false") {
                            removeRectangle();

                            new google.maps.Marker({
                                map: locationMap,
                                position: results.data.geometry.location
                            });

                            lat = results.data.geometry.location.lat;
                            lng = results.data.geometry.location.lng;

                            $scope.address.lat = lat;
                            $scope.address.lng = lng;

                            geoCoordLabel = angular.element(document.querySelector('#geocoordinates'));
                            geoCoordLabel.html("Geo Coordinate: " + lat + "," + lng);

                            geoAddressLabel = angular.element(document.querySelector('#geoaddress'));
                            geoAddressLabel.html("Geo Address: " + fullAddress);

                            $scope.latlng = true;

                            if (results.data.geometry.viewport) {

                                rectangle = new google.maps.Rectangle({
                                    strokeColor: '#FF0000',
                                    strokeOpacity: 0.8,
                                    strokeWeight: 0.5,
                                    fillColor: '#FF0000',
                                    fillOpacity: 0.35,
                                    map: locationMap,
                                    bounds: {
                                        north: results.data.geometry.viewport.northeast.lat,
                                        south: results.data.geometry.viewport.southwest.lat,
                                        east: results.data.geometry.viewport.northeast.lng,
                                        west: results.data.geometry.viewport.southwest.lng
                                    }
                                });

                                var googleBounds = new google.maps.LatLngBounds(results.data.geometry.viewport.southwest, results.data.geometry.viewport.northeast);

                                locationMap.setCenter(new google.maps.LatLng(lat, lng));
                                locationMap.fitBounds(googleBounds);
                            }
                        } else {
                            errorLabel = angular.element(document.querySelector('#lt'));
                            errorLabel.html("Place not found.");
                            $scope.latlng = true;
                            removeRectangle();
                        }

                    }, function errorCallback(results) {
                       console.log(results);
                    });
                }
                . . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save the file, but keep it open for now. If you were to visit the application in your browser again, you wouldn't see any new changes to its appearance or behavior. Likewise, if you were to enter an address and click on the &lt;strong&gt;Generate&lt;/strong&gt; button, the application still would not generate or display a mapcode. This is because we must still edit a few files before the mapcode functionality will work. Let's continue to make these changes, and also take a closer look at how these mapcodes are generated.&lt;/p&gt;

&lt;h2 id="step-7-—-understanding-mapcode-generation"&gt;Step 7 — Understanding Mapcode Generation&lt;/h2&gt;

&lt;p&gt;While still looking at the &lt;code&gt;createDigitalAddressApp.js&lt;/code&gt; file, scroll past the section of code that you added in the previous step to find the code that takes the information submitted through the form and process it into a unique mapcode. Whenever a user clicks the &lt;strong&gt;Generate&lt;/strong&gt; button, the code within the &lt;code&gt;index.php&lt;/code&gt; file submits the form and calls the &lt;code&gt;processForm&lt;/code&gt; function, which is defined here in &lt;code&gt;createDigitalAddressApp.js&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-label " title="/var/www/html/digiaddress/js/createDigitalAddressApp.js"&gt;/var/www/html/digiaddress/js/createDigitalAddressApp.js&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .
$scope.processForm = function () {
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;processForm&lt;/code&gt; then makes an HTTP POST to the &lt;code&gt;generateDigitalAddress.php&lt;/code&gt; file:&lt;/p&gt;
&lt;div class="code-label " title="/var/www/html/digiaddress/js/createDigitalAddressApp.js"&gt;/var/www/html/digiaddress/js/createDigitalAddressApp.js&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .
$http({
    method: 'POST',
    url: 'generateDigitalAddress.php',
    data: $scope.address,
    headers: {'Content-Type': 'application/x-www-form-urlencoded'}
}).then(function (response) {
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;a href="http://www.mapcode.com/aboutus.html"&gt;Stichting Mapcode Foundation&lt;/a&gt; provides the API that generates mapcodes from physical addresses as a free web service. To understand how this call to the Mapcode web service works, close &lt;code&gt;createDigitalAddressApp.js&lt;/code&gt; and open the &lt;code&gt;generateDigitialAddress.php&lt;/code&gt; file:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nano /var/www/html/digiaddress/generateDigitalAddress.php
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At the top of the file, you'll see the following:&lt;/p&gt;
&lt;div class="code-label " title="/var/www/html/digiaddress/generateDigitalAddress.php"&gt;/var/www/html/digiaddress/generateDigitalAddress.php&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&amp;lt;?php
include("db.php");
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The line reading &lt;code&gt;include("db.php");&lt;/code&gt; tells PHP to &lt;em&gt;include&lt;/em&gt; all the text, code, and markup from the &lt;code&gt;db.php&lt;/code&gt; file within the &lt;code&gt;generateDigitalAddress.php&lt;/code&gt; file. &lt;code&gt;db.php&lt;/code&gt; holds the login credentials for the MySQL database you created in Step 2, and by including it within &lt;code&gt;generateDigitalAddress.php&lt;/code&gt;, we can add any address information submitted through the form to the database.&lt;/p&gt;

&lt;p&gt;Below this &lt;code&gt;include&lt;/code&gt; statement are a few more lines that obtain the latitude and longitude information based on the request submitted by &lt;code&gt;createDigitalAddressApp.js&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-label " title="/var/www/html/digiaddress/generateDigitalAddress.php"&gt;/var/www/html/digiaddress/generateDigitalAddress.php&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .
$data = json_decode(file_get_contents("php://input"));
$lat = $data-&amp;gt;lat;
$long = $data-&amp;gt;lng;
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Look for the following comment in &lt;code&gt;generateDigitalAddress.php&lt;/code&gt; file.&lt;/p&gt;
&lt;div class="code-label " title="/var/www/html/digiaddress/generateDigitalAddress.php"&gt;/var/www/html/digiaddress/generateDigitalAddress.php&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .
// call to mapcode web service
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add the following line of code below this comment. This code makes a call the Mapcode API, sending &lt;code&gt;lat&lt;/code&gt; and &lt;code&gt;long&lt;/code&gt; as parameters.&lt;/p&gt;
&lt;div class="code-label " title="/var/www/html/digiaddress/generateDigitalAddress.php"&gt;/var/www/html/digiaddress/generateDigitalAddress.php&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .
// call to mapcode web service
&lt;span class="highlight"&gt;$digitaldata = file_get_contents("https://api.mapcode.com/mapcode/codes/".$lat.",".$long."?include=territory,alphabet&amp;amp;allowLog=true&amp;amp;client=web");&lt;/span&gt;
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The web service returns the JSON data which was assigned to &lt;code&gt;digitaldata&lt;/code&gt;, and the following statement decodes that JSON:&lt;/p&gt;
&lt;div class="code-label " title="/var/www/html/digiaddress/generateDigitalAddress.php"&gt;/var/www/html/digiaddress/generateDigitalAddress.php&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .
$digitalAddress["status"] = json_decode($digitaldata, TRUE)['local']['territory']." ".json_decode($digitaldata, TRUE)['local']['mapcode'];
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This returns a mapcode for the user-specified location. The following lines then store this information in the database:&lt;/p&gt;
&lt;div class="code-label " title="/var/www/html/digiaddress/generateDigitalAddress.php"&gt;/var/www/html/digiaddress/generateDigitalAddress.php&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .
$obj = new databaseConnection();

$conn = $obj-&amp;gt;dbConnect();

$obj-&amp;gt;insertLocation($conn, $digitalAddress["status"],$data-&amp;gt;state,$data-&amp;gt;zip,$data-&amp;gt;street,$data-&amp;gt;town,$data-&amp;gt;house,$lat,$long);
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, the final line echoes the mapcode back to the caller function:&lt;/p&gt;
&lt;div class="code-label " title="/var/www/html/digiaddress/generateDigitalAddress.php"&gt;/var/www/html/digiaddress/generateDigitalAddress.php&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .
echo json_encode($digitalAddress);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save and close this file, then reopen &lt;code&gt;createDigitalAddressApp.js&lt;/code&gt; again:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nano /var/www/html/digiaddress/js/createDigitalAddressApp.js
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When a mapcode has been retrieved successfully, the following lines in the &lt;code&gt;createDigitalAddressApp.js&lt;/code&gt; file displays it to the user in a dialog box:&lt;/p&gt;
&lt;div class="code-label " title="/var/www/html/digiaddress/js/createDigitalAddressApp.js"&gt;/var/www/html/digiaddress/js/createDigitalAddressApp.js&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .
digiAddress = response.data.status;
. . .
$('#digitalAddressDialog').modal('show');
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Although you did add a new line of code to &lt;code&gt;generateDigitalAddress.php&lt;/code&gt;, you still won't see any functional changes when you visit and interact with the app in your browser. This is because you've not yet added your Google API key to the &lt;code&gt;geoimplement.php&lt;/code&gt; file, which makes the actual call to the Google Maps API.&lt;/p&gt;

&lt;h2 id="step-8-—-enabling-calls-to-the-google-maps-api"&gt;Step 8 — Enabling Calls to the Google Maps API&lt;/h2&gt;

&lt;p&gt;This application depends on the Google Maps API to translate a physical address into the appropriate latitude and longitude coordinates. These are then passed on to the Mapcode API which uses them to generate a mapcode. Consequently, if the application is unable to communicate with the Google Maps API to generate the location's latitude and longitude, any attempt to generate a mapcode will fail.&lt;/p&gt;

&lt;p&gt;Recall from Step 6 where, after constructing the &lt;code&gt;address&lt;/code&gt; data, we passed the result along via an HTTP POST request in the &lt;code&gt;createDigitalAddressApp.js&lt;/code&gt; file:&lt;/p&gt;
&lt;div class="code-label " title="/var/www/html/digiaddress/js/createDigitalAddressApp.js"&gt;/var/www/html/digiaddress/js/createDigitalAddressApp.js&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;$http({
    method: 'POST',
    url: 'geoimplement.php',
    data: {address: fullAddress},
    headers: {'Content-Type': 'application/x-www-form-urlencoded'}
}).then(function successCallback(results) {
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This code block sends the address data entered by a user to the &lt;code&gt;geoimplement.php&lt;/code&gt; file which contains the code that calls the Google Maps API. Go ahead and open this file:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nano /var/www/html/digiaddress/geoimplement.php
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You'll see that it first decodes the &lt;code&gt;address&lt;/code&gt; that was received through the POST request:&lt;/p&gt;
&lt;div class="code-label " title="/var/www/html/digiaddress/geoimplement.php"&gt;/var/www/html/digiaddress/geoimplement.php&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .
$data=json_decode(file_get_contents("php://input"));
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It then passes the &lt;code&gt;address&lt;/code&gt; field of the input data to a &lt;code&gt;geocode&lt;/code&gt; function which returns the geographic information on the address:&lt;/p&gt;
&lt;div class="code-label " title="/var/www/html/digiaddress/geoimplement.php"&gt;/var/www/html/digiaddress/geoimplement.php&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .
$result = geocode($data-&amp;gt;address);
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The result is then echoed back to the caller:&lt;/p&gt;
&lt;div class="code-label " title="/var/www/html/digiaddress/geoimplement.php"&gt;/var/www/html/digiaddress/geoimplement.php&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .
echo json_encode($result);
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;geocode&lt;/code&gt; function encodes the &lt;code&gt;address&lt;/code&gt; and passes it on to the Google Maps API, along with your application key:&lt;/p&gt;
&lt;div class="code-label " title="/var/www/html/digiaddress/geoimplement.php"&gt;/var/www/html/digiaddress/geoimplement.php&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .
// url encode the address
$address = urlencode($address);

// google map geocode api url
$url = "https://maps.googleapis.com/maps/api/geocode/json?address={$address}&amp;amp;key=&lt;span class="highlight"&gt;&amp;lt;YOUR KEY&amp;gt;&lt;/span&gt;";
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Before scrolling on, go ahead and add your API key to the line under the &lt;code&gt;// google map geocode api url&lt;/code&gt; comment:&lt;/p&gt;
&lt;div class="code-label " title="/var/www/html/digiaddress/geoimplement.php"&gt;/var/www/html/digiaddress/geoimplement.php&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .
// google map geocode api url
$url = "https://maps.googleapis.com/maps/api/geocode/json?address={$address}&amp;amp;key=&lt;span class="highlight"&gt;ExampleAPIKeyH2vITfv1eIHbfka9ym634Esw7u&lt;/span&gt;";
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After sending the call to the Google Maps API, the response is decoded and its value is returned by the function:&lt;/p&gt;
&lt;div class="code-label " title="/var/www/html/digiaddress/geoimplement.php"&gt;/var/www/html/digiaddress/geoimplement.php&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .
// get the json response
$resp_json = file_get_contents($url);

// decode the json
$resp = json_decode($resp_json, true);

if ($resp['status'] == 'OK') {
    return $resp['results'][0];
} else {
    return false;
}
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save this file, and visit your application once again. Input &lt;code&gt;US-NY&lt;/code&gt; in the state field and then hit &lt;code&gt;TAB&lt;/code&gt; to change the input focus to the next field. You will see the following output:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/mapcode/final_finalmap.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Notice that the geocoordinates and physical address that you entered in the form appear underneath the map. This makes the application feel much more engaging and interactive.&lt;/p&gt;

&lt;span class='note'&gt;&lt;p&gt;
&lt;strong&gt;Note:&lt;/strong&gt; When it comes to abbreviations for place names, Mapcode uses the ISO 3166 standard. This means that it may not interpret some commonly-used abbreviations as expected. For example, if you'd like to generate a Mapcode for an address in Louisiana and you enter &lt;code&gt;LA&lt;/code&gt;, the map will jump to Los Angeles, California (rather than the state of Louisiana).&lt;/p&gt;

&lt;p&gt;You can avoid confusion with US postal abbreviations by preceding them with &lt;code&gt;US-&lt;/code&gt;. In the context of this Louisiana example, you would enter &lt;code&gt;US-LA&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To learn more about how Mapcode uses this standard, check out the &lt;a href="http://www.mapcode.com/isos.html"&gt;Territories and standard codes reference page&lt;/a&gt;.&lt;br&gt;&lt;/p&gt;&lt;/span&gt;

&lt;p&gt;Despite this improvement to how the application displays locations on the map, the app still isn't fully functional. The last step you need to take before you can generate a mapcode is to edit the &lt;code&gt;db.php&lt;/code&gt; file to allow the application to access your database.&lt;/p&gt;

&lt;h2 id="step-9-—-adding-database-credentials-and-testing-mapcode-generation"&gt;Step 9 — Adding Database Credentials and Testing Mapcode Generation&lt;/h2&gt;

&lt;p&gt;Recall that this application stores every address entered into the form — along with its latitude, longitude, and mapcode — in the database you created in Step 2. This is made possible by the code within the &lt;code&gt;db.php&lt;/code&gt; file, which stores your database credentials and allows the application to access the &lt;code&gt;locations&lt;/code&gt; table within it.&lt;/p&gt;

&lt;p&gt;As a final step to enable the mapcode generation functionality, open the &lt;code&gt;db.php&lt;/code&gt; file for editing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nano /var/www/html/digiaddress/db.php
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Near the top of this file, find the line that begins with &lt;code&gt;$pass&lt;/code&gt;. This line submits your MySQL login credentials in order to allow the application to access your database. Replace &lt;code&gt;&lt;span class="highlight"&gt;your_password&lt;/span&gt;&lt;/code&gt; with your &lt;strong&gt;root&lt;/strong&gt; MySQL user's password:&lt;/p&gt;
&lt;div class="code-label " title="/var/www/html/digiaddress/db.php"&gt;/var/www/html/digiaddress/db.php&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .
        $username = "root";
        $pass = "&lt;span class="highlight"&gt;your_password&lt;/span&gt;";
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That is the last change you need to make in order to generate a mapcode from a physical address. Save and close the file, then go ahead and refresh the application in your browser once again. Enter in an address of your choice and click the &lt;strong&gt;Generate&lt;/strong&gt; button. The output will look similar to this:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/mapcode/final_digitaladdress.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;At this stage, you have completed your application and you can now generate a short digital address for any physical location in the world. Feel free to experiment with different addresses, and note that the address you enter does not necessarily need to be within the United States.&lt;/p&gt;

&lt;p&gt;Your final task is to enable this app's second functionality: retrieving an address from the database using its respective mapcode.&lt;/p&gt;

&lt;h2 id="step-10-—-retrieving-a-physical-address"&gt;Step 10 — Retrieving a Physical Address&lt;/h2&gt;

&lt;p&gt;Now that you're able to generate a mapcode from a given physical address, your final step is to retrieve the original physical address, as derived from the mapcode. To accomplish this, we will develop a PHP user interface, shown here:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/mapcode/final_getaddress.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;The code for this UI is available in the &lt;code&gt;findaddress.php&lt;/code&gt; file. As the UI defined within this file is fairly similar to the UI we covered earlier in Step 4, we will not look too closely at all the details of how it works. We will, however, go through these three files to explain generally how they function.&lt;/p&gt;

&lt;p&gt;In order to enable the address retrieval functionality, you'll need to add your Google API key to the &lt;code&gt;findaddress.php&lt;/code&gt; file, so open it up with your preferred editor:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nano /var/www/html/digiaddress/findaddress.php
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Near the bottom of the file, find the line that begins with &lt;code&gt;&amp;lt;script async defer src=&lt;/code&gt;. It will look like this:&lt;/p&gt;
&lt;div class="code-label " title="/var/www/html/digiaddress/findaddress.php"&gt;/var/www/html/digiaddress/findaddress.php&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&amp;lt;script async defer src="https://maps.googleapis.com/maps/api/js?key=&lt;span class="highlight"&gt;&amp;lt;YOUR KEY&amp;gt;&lt;/span&gt;"&amp;gt;&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Replace &lt;code&gt;&lt;span class="highlight"&gt;&amp;lt;YOUR KEY&amp;gt;&lt;/span&gt;&lt;/code&gt; with your Google API key as you've done in the previous steps, then save the file. Before closing it, though, let's take a quick look to see how these files work together.&lt;/p&gt;

&lt;p&gt;When a user submits the form it triggers a &lt;code&gt;submit&lt;/code&gt; event, and an event listener calls the &lt;code&gt;fetchadd&lt;/code&gt; function:&lt;/p&gt;
&lt;div class="code-label " title="/var/www/html/digiaddress/findaddress.php"&gt;/var/www/html/digiaddress/findaddress.php&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .
&amp;lt;form ng-submit="fetchadd()" class="custom-form"&amp;gt;
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;fetchadd&lt;/code&gt; function sends the digital address to &lt;code&gt;fetchaddress.php&lt;/code&gt; with a POST request:&lt;/p&gt;
&lt;div class="code-label " title="/var/www/html/digiaddress/js/findAddressApp.js"&gt;/var/www/html/digiaddress/js/findAddressApp.js&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .
$http({
    method : 'POST',
    url : 'fetchaddress.php',
    data : {digiaddress: $scope.digiaddress}
}).then(function(response){
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If the POST is successful, the function returns a JSON response. The following line parses this response:&lt;/p&gt;
&lt;div class="code-label " title="/var/www/html/digiaddress/js/findAddressApp.js"&gt;/var/www/html/digiaddress/js/findAddressApp.js&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .
var jsonlatlng = JSON.parse(response.data.latlng);
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The next lines set the marker on the map:&lt;/p&gt;
&lt;div class="code-label " title="/var/www/html/digiaddress/js/findAddressApp.js"&gt;/var/www/html/digiaddress/js/findAddressApp.js&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .
marker = new google.maps.Marker({
    position: new google.maps.LatLng(jsonlatlng.latitude, jsonlatlng.longitude),
        map: locationMap
});
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And the following prints the geocoordinates and the physical address:&lt;/p&gt;
&lt;div class="code-label " title="/var/www/html/digiaddress/js/findAddressApp.js"&gt;/var/www/html/digiaddress/js/findAddressApp.js&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .
geoCoordLabel = angular.element(document.querySelector('#geocoordinates'));
geoCoordLabel.html("Geo Coordinate: "+ jsonlatlng.latitude +","+ jsonlatlng.longitude);

geoAddressLabel = angular.element(document.querySelector('#geoaddress'));
geoAddressLabel.html("Geo Address: " + jsonlatlng.house +","+ jsonlatlng.town +","+ jsonlatlng.street +","+ jsonlatlng.state + " " + jsonlatlng.zip );
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Visit this application in your browser by going to the following link:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;http://&lt;span class="highlight"&gt;your_server_ip&lt;/span&gt;/digiaddress/findaddress.php
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Test it out by entering in the mapcode you obtained earlier. The following figure shows a typical output:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/mapcode/final_fetchedaddress.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;With that, your application is finished. You can now create a unique mapcode for any location in the world, and then use that mapcode to retrieve the location's physical address.&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;In this tutorial you used the Google Maps API to pin a location and gets its longitude, latitude information. This information is used to generate a unique and short digital address using Mapcode API. There are a number of practical use cases for mapcodes, ranging from emergency services to archaeological surveying. &lt;a href="http://www.mapcode.com/aboutus.html"&gt;The Stichting Mapcode Foundation&lt;/a&gt; lists several such use cases.&lt;/p&gt;

&lt;h3 id="acknowledgements"&gt;Acknowledgements&lt;/h3&gt;

&lt;p&gt;Many thanks to &lt;a href="https://www.linkedin.com/in/dineshkarpe"&gt;Dinesh Karpe&lt;/a&gt; and &lt;a href="https://www.linkedin.com/in/ptsayli"&gt;Sayli Patil&lt;/a&gt; for developing the entire project code.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-retrieve-let-s-encrypt-ssl-wildcard-certificates-using-cloudflare-validation-on-centos-7</id>
    <published>2018-08-13T15:54:36Z</published>
    <updated>2018-08-16T15:06:54Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-retrieve-let-s-encrypt-ssl-wildcard-certificates-using-cloudflare-validation-on-centos-7"/>
    <title>How to Retrieve Let's Encrypt SSL Wildcard Certificates using CloudFlare Validation on CentOS 7</title>
    <content type="html">&lt;p&gt;&lt;em&gt;The author selected &lt;a href="https://www.brightfunds.org/organizations/code-org"&gt;Code.org&lt;/a&gt; to receive a donation as part of the &lt;a href="https://do.co/w4do-cta"&gt;Write for DOnations&lt;/a&gt; program.&lt;/em&gt;&lt;/p&gt;

&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://letsencrypt.org/"&gt;Let's Encrypt&lt;/a&gt; is a certificate authority (CA) that provides free certificates for &lt;a href="https://www.digitalocean.com/community/tutorials/openssl-essentials-working-with-ssl-certificates-private-keys-and-csrs"&gt;Transport Layer Security (TLS) encryption&lt;/a&gt;. It provides a software client called &lt;a href="https://certbot.eff.org/"&gt;Certbot&lt;/a&gt; which simplifies the process of certificate creation, validation, signing, installation, and renewal.&lt;/p&gt;

&lt;p&gt;Let’s Encrypt now supports &lt;a href="https://en.wikipedia.org/wiki/Wildcard_certificate"&gt;wildcard certificates&lt;/a&gt; which allow you to secure all subdomains of a domain with a single certificate. This will be useful if you want to host multiple services, such as web interfaces,  APIs, and other sites using a single server.&lt;/p&gt;

&lt;p&gt;To obtain a wildcard certificate from Let’s Encrypt you have to use one of Certbot’s &lt;a href="https://certbot.eff.org/docs/using.html#dns-plugins"&gt;DNS plugins&lt;/a&gt;, which include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;certbot-dns-cloudflare&lt;/li&gt;
&lt;li&gt;certbot-dns-route53&lt;/li&gt;
&lt;li&gt;certbot-dns-google&lt;/li&gt;
&lt;li&gt;certbot-dns-digitalocean&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The plugin you choose depends on which service hosts your DNS records. In this tutorial you will obtain a wildcard certificate for your domain using &lt;a href="https://cloudflare.com"&gt;CloudFlare&lt;/a&gt; validation with Certbot on CentOS 7. You'll then configure the certificate to renew it when it expires.&lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;To complete this tutorial, you'll need the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One CentOS 7 server set up by following &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-centos-7"&gt;the CentOS 7 initial server setup guide&lt;/a&gt;, including a sudo non-root user and a firewall.&lt;/li&gt;
&lt;li&gt;A fully registered domain name. You can purchase a domain name on &lt;a href="https://namecheap.com/"&gt;Namecheap&lt;/a&gt;, get one for free on &lt;a href="http://www.freenom.com/en/index.html"&gt;Freenom&lt;/a&gt;, or use the domain registrar of your choice.&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://www.cloudflare.com/"&gt;Cloudflare&lt;/a&gt; account.&lt;/li&gt;
&lt;li&gt;A DNS record set up for your domain in Cloudflare's DNS, along with a couple of subdomains configured. You can follow &lt;a href="https://support.cloudflare.com/hc/en-us/articles/201720164-Step-2-Create-a-Cloudflare-account-and-add-a-website"&gt;CloudFlare's tutorial on setting up a web site&lt;/a&gt; to configure this.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="step-1-—-installing-certbot"&gt;Step 1 — Installing Certbot&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;certbot&lt;/code&gt; package is not available through CentOS's package manager by default. You will need to enable the &lt;a href="https://fedoraproject.org/wiki/EPEL"&gt;EPEL&lt;/a&gt; repository to install Certbot and its plugins.&lt;/p&gt;

&lt;p&gt;To add the CentOS 7 EPEL repository, run the following command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo yum install -y epel-release
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once the installation completes, you can install &lt;code&gt;certbot&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo yum install -y certbot
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And then install the CloudFlare plugin for Certbot:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo yum install -y python2-cloudflare python2-certbot-dns-cloudflare
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;span class='note'&gt;&lt;p&gt;&lt;/p&gt;

&lt;p&gt;If you are using another DNS service, you can find the corresponding plugin using the &lt;code&gt;yum search&lt;/code&gt; command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;yum search python2-certbot-dns
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;/p&gt;&lt;/span&gt;

&lt;p&gt;You've prepared your server to obtain certificates. Now you need to get the API key from CloudFlare.&lt;/p&gt;

&lt;h2 id="step-2-—-getting-the-cloudflare-api"&gt;Step 2 — Getting the CloudFlare API&lt;/h2&gt;

&lt;p&gt;In order for Certbot to automatically renew wildcard certificates, you need to provide it with your CloudFlare login and API key.  &lt;/p&gt;

&lt;p&gt;Log in to your Cloudflare account and navigate to the &lt;a href="https://www.cloudflare.com/a/profile"&gt;Profile page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Click the &lt;strong&gt;View&lt;/strong&gt; button in the &lt;strong&gt;Global API Key&lt;/strong&gt; line.&lt;/p&gt;

&lt;p&gt;&lt;img src="https://i.imgur.com/VDyv79i.png" alt="CloudFlare Profile - API Keys"&gt;&lt;/p&gt;

&lt;p&gt;For security reasons, you will be asked to re-enter your Cloudflare account password. Enter it and validate the CAPTCHA. Then click the &lt;strong&gt;View&lt;/strong&gt; button again. You'll see your API key:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://i.imgur.com/wjqbplX.png" alt="CloudFlare Profile - API Keys"&gt;&lt;/p&gt;

&lt;p&gt;Copy this key. You will use it in the next step. &lt;/p&gt;

&lt;p&gt;Now return to your server to continue the process of obtaining the certificate.  &lt;/p&gt;

&lt;h2 id="step-3-—-configuring-certbot"&gt;Step 3 — Configuring Certbot&lt;/h2&gt;

&lt;p&gt;You have all of the necessary information to tell Certbot how to use Cloudflare, but let's write it to a configuration file so that Сertbot can use it automatically.&lt;/p&gt;

&lt;p&gt;First run the &lt;code&gt;certbot&lt;/code&gt; command without any parameters to create the initial configuration file:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo certbot
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next create a configuration file in the &lt;code&gt;/etc/letsencrypt&lt;/code&gt; directory which will contain your CloudFlare email and API key:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo vi /etc/letsencrypt/cloudflareapi.cfg
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add the following into it, replacing the placeholders with your Cloudflare login and API key:&lt;/p&gt;
&lt;div class="code-label " title="/etc/letsencrypt/cloudflareapi.cfg"&gt;/etc/letsencrypt/cloudflareapi.cfg&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;dns_cloudflare_email = &lt;span class="highlight"&gt;your_cloudflare_login&lt;/span&gt;
dns_cloudflare_api_key = &lt;span class="highlight"&gt;your_cloudflare_api_key&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save the file and exit the editor.&lt;br&gt;
With Cloudflare's API key, you can do the same things from the command line that you can do from the Cloudflare UI, so in order to protect your account, make the configuration file readable only by its owner so nobody else can obtain your key:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo chmod 600 /etc/letsencrypt/cloudflareapi.cfg
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With the configuration files in place, let's obtain a certificate.&lt;/p&gt;

&lt;h2 id="step-4-—-obtaining-the-certificate"&gt;Step 4 — Obtaining the Certificate&lt;/h2&gt;

&lt;p&gt;To obtain a certificate, we'll use the &lt;code&gt;certbot&lt;/code&gt; command and specify the plugin we want, the credentials file we want to use, and the server we should use to handle the request.  By default, Certbot uses Let’s Encrypt’s production servers, which use &lt;a href="https://en.wikipedia.org/wiki/Automated_Certificate_Management_Environment"&gt;ACME&lt;/a&gt; API version 1, but Certbot uses another protocol for obtaining wildcard certificates, so you need to provide an ACME v2 endpoint.&lt;/p&gt;

&lt;p&gt;Run the following command to obtain the wildcard certificate for your domain:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo certbot certonly --cert-name &lt;span class="highlight"&gt;your_domain&lt;/span&gt; --dns-cloudflare --dns-cloudflare-credentials /etc/letsencrypt/cloudflareapi.cfg --server &lt;span class="highlight"&gt;https://acme-v02.api.letsencrypt.org/directory&lt;/span&gt; -d "*.&lt;span class="highlight"&gt;your_domain&lt;/span&gt;" -d &lt;span class="highlight"&gt;your_domain&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will be asked to specify the email address that should receive urgent renewal and security notices:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;...
Plugins selected: Authenticator dns-cloudflare, Installer None
Enter email address (used for urgent renewal and security notices) (Enter 'c' to
cancel): &lt;span class="highlight"&gt;your email&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then you'll be asked to agree to the Terms of Service:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;-------------------------------------------------------------------------------
Please read the Terms of Service at
https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf. You must
agree in order to register with the ACME server at
https://acme-v02.api.letsencrypt.org/directory
-------------------------------------------------------------------------------
(A)gree/(C)ancel: &lt;span class="highlight"&gt;A&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then you'll be asked to share your email address with the Electronic Frontier&lt;br&gt;
Foundation:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;-------------------------------------------------------------------------------
Would you be willing to share your email address with the Electronic Frontier
Foundation, a founding partner of the Let's Encrypt project and the non-profit
organization that develops Certbot? We'd like to send you email about EFF and
our work to encrypt the web, protect its users and defend digital rights.
-------------------------------------------------------------------------------
(Y)es/(N)o: &lt;span class="highlight"&gt;N&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then Certbot will obtain your certificates. You will see the following message:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/&lt;span class="highlight"&gt;your_domain&lt;/span&gt;/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/&lt;span class="highlight"&gt;your_domain&lt;/span&gt;/privkey.pem
   Your cert will expire on 2018-07-31. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot
   again. To non-interactively renew *all* of your certificates, run
   "certbot renew"
 - Your account credentials have been saved in your Certbot
   configuration directory at /etc/letsencrypt. You should make a
   secure backup of this folder now. This configuration directory will
   also contain certificates and private keys obtained by Certbot so
   making regular backups of this folder is ideal.
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now you have your wildcard certificate. Let's take a look at what Certbot has downloaded for you. Use the &lt;code&gt;ls&lt;/code&gt; command to see the contents of the directory that holds your keys and certificates:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ls /etc/letsencrypt/live/&lt;span class="highlight"&gt;your_domain&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;cert.pem  chain.pem  fullchain.pem  privkey.pem  README
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;README&lt;/code&gt; file contains information about these files:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;$ cat /etc/letsencrypt/live/&lt;span class="highlight"&gt;your_domain&lt;/span&gt;/README
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You'll see output like this:&lt;/p&gt;
&lt;div class="code-label " title="README"&gt;README&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;This directory contains your keys and certificates.

`privkey.pem`  : the private key for your certificate.
`fullchain.pem`: the certificate file used in most server software.
`chain.pem`    : used for OCSP stapling in Nginx &amp;gt;=1.3.7.
`cert.pem`     : will break many server configurations, and should not be used
                 without reading further documentation (see link below).

We recommend not moving these files. For more information, see the Certbot
User Guide at https://certbot.eff.org/docs/using.html#where-are-my-certificates.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;From here, you can configure your servers with the wildcard certificate. You'll usually only need two of these files: &lt;code&gt;fullchain.pem&lt;/code&gt; and &lt;code&gt;privkey.pem&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For example, you can configure several web-based services:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;wwww.&lt;span class="highlight"&gt;example.com&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;api.&lt;span class="highlight"&gt;example.com&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;mail.&lt;span class="highlight"&gt;example.com&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To do this, you will need a web server, such as Apache or Nginx. The installation and configuration of these servers is beyond the scope of this tutorial, but the following guides will walk you through all the necessary steps to configure the servers and apply your certificates.&lt;/p&gt;

&lt;p&gt;For Nginx, take a look at these tutorials:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-nginx-on-centos-7"&gt;How To Install Nginx on CentOS 7&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.digitalocean.com/community/tutorials/how-to-set-up-nginx-server-blocks-on-centos-7"&gt;How To Set Up Nginx Server Blocks on CentOS 7&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.digitalocean.com/community/tutorials/how-to-create-a-self-signed-ssl-certificate-for-nginx-on-centos-7#step-3-configure-nginx-to-use-ssl"&gt;Configure Nginx to Use SSL&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For Apache, consult these tutorials:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-linux-apache-mysql-php-lamp-stack-on-centos-7#step-one-%E2%80%94-install-apache"&gt;How To Install Apache On CentOS 7&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.digitalocean.com/community/tutorials/how-to-set-up-apache-virtual-hosts-on-centos-7"&gt;How To Set Up Apache Virtual Hosts on CentOS 7&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.digitalocean.com/community/tutorials/how-to-create-an-ssl-certificate-on-apache-for-centos-7"&gt;How To Create an SSL Certificate on Apache for CentOS 7&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now let's look at renewing the certificates automatically.&lt;/p&gt;

&lt;h2 id="step-5-—-renewing-certificates"&gt;Step 5 — Renewing certificates&lt;/h2&gt;

&lt;p&gt;Let’s Encrypt issues short-lived certificates which are valid for 90 days. We'll need to set up a cron task to check for expiring certificates and renew them automatically.&lt;/p&gt;

&lt;p&gt;Let's create a &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-schedule-routine-tasks-with-cron-and-anacron-on-a-vps"&gt;cron task&lt;/a&gt;&lt;br&gt;
which will run the renewal check daily.&lt;/p&gt;

&lt;p&gt;Use the following command to open the &lt;code&gt;crontab&lt;/code&gt; file for editing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo crontab -e
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add the following line to the file to attempt to renew the certificates daily:&lt;/p&gt;
&lt;div class="code-label " title="crontab"&gt;crontab&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;30 2 * * * certbot renew --noninteractive
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;30 2 * * *&lt;/code&gt; means "run the following command at 2:30 am, every day". &lt;/li&gt;
&lt;li&gt;The &lt;code&gt;certbot renew&lt;/code&gt; command  will check all certificates installed on the system and update any that are set to expire in less than thirty days.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--noninteractive&lt;/code&gt; tells Certbot not to wait for user input.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You will need to reload your web server after updating your certificates. The &lt;code&gt;renew&lt;/code&gt; command includes hooks for running commands or scripts before or after a certificate is renewed. You can also configure these hooks in the renewal configuration file for your domain.&lt;/p&gt;

&lt;p&gt;For example, to reload your Nginx server, open the renewal configuration file:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo vi /etc/letsencrypt/renewal/&lt;span class="highlight"&gt;your_domain&lt;/span&gt;.conf
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then add the following line under the &lt;code&gt;[renewalparams]&lt;/code&gt; section:&lt;/p&gt;
&lt;div class="code-label " title="/etc/letsencrypt/renewal/&amp;lt;span class=" highlight&gt;your_domain.conf'&amp;gt;/etc/letsencrypt/renewal/&lt;span class="highlight"&gt;your_domain&lt;/span&gt;.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;span class="highlight"&gt;renew_hook = systemctl reload nginx&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now Certbot will automatically restart your web server after installing the updated certificate.&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;In this tutorial you've installed the Certbot client, obtained your wildcard certificate using DNS validation and enabled automatic renewals. This will allow you to use a single certificate with multiple subdomains of your domain and secure your web services.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/como-fazer-scraping-em-paginas-web-com-beautiful-soup-and-python-3-pt</id>
    <published>2018-07-09T21:46:48Z</published>
    <updated>2018-08-15T20:17:27Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/como-fazer-scraping-em-paginas-web-com-beautiful-soup-and-python-3-pt"/>
    <title>Como Fazer Scraping em Páginas Web com Beautiful Soup and Python 3</title>
    <content type="html">&lt;h3 id="introdução"&gt;Introdução&lt;/h3&gt;

&lt;p&gt;Muitos projetos de análise de dados, big data, e aprendizado de máquina exigem o scraping de websites para coletar os dados com os quais você irá trabalhar. A linguagem de programação Python é largamente utilizada na comunidade de data science, e, portanto, tem um ecossistema de módulos e ferramentas que você pode usar em seus próprios projetos. Neste tutorial estaremos nos concentrando no módulo Beautiful Soup.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.crummy.com/software/BeautifulSoup/"&gt;Beautiful Soup&lt;/a&gt;, uma alusão à música &lt;a href="https://en.wikipedia.org/wiki/Mock_Turtle"&gt;Mock Turtle’s&lt;/a&gt; encontrada no Capítulo 10 de &lt;em&gt;Alice no País das Maravilhas&lt;/em&gt;, de Lewis Carroll, é uma biblioteca do Python que permite um retorno rápido em projetos de web scraping. Atualmente disponível como Beautiful Soup 4 e compatível tanto com Python 2.7 quanto com Python 3, o Beautiful Soup cria uma árvore de análise a partir de documentos HTML e XML analisados (incluindo documentos com tags não fechadas ou &lt;a href="https://en.wikipedia.org/wiki/Tag_soup"&gt;tag soup&lt;/a&gt; e outras marcações malformadas).&lt;/p&gt;

&lt;p&gt;Neste tutorial, iremos coletar e analisar uma página web de forma a pegar dados textuais e gravar as informações que tivermos recolhido em um arquivo CSV.&lt;/p&gt;

&lt;h2 id="pré-requisitos"&gt;Pré-requisitos&lt;/h2&gt;

&lt;p&gt;Antes de trabalhar com este tutorial, você deve ter um ambiente de programação Python &lt;a href="https://www.digitalocean.com/community/tutorial_series/how-to-install-and-set-up-a-local-programming-environment-for-python-3"&gt;local&lt;/a&gt; ou &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-python-3-and-set-up-a-programming-environment-on-an-ubuntu-16-04-server"&gt;baseado em servidor&lt;/a&gt; configurado em sua máquina.&lt;/p&gt;

&lt;p&gt;Você deve ter os módulos Requests e Beautiful Soup &lt;strong&gt;instalados&lt;/strong&gt;, o que pode ser conseguido seguindo o nosso tutorial “&lt;a href="https://www.digitalocean.com/community/tutorials/how-to-work-with-web-data-using-requests-and-beautiful-soup-with-python-3"&gt;How To Work with Web Data Using Requests and Beautiful Soup with Python 3&lt;/a&gt;.” Também seria útil ter familiaridade no trabalho com esses módulos.&lt;/p&gt;

&lt;p&gt;Adicionalmente, uma vez que vamos trabalhar com dados extraídos da web, você deve estar confortável com a estrutura e a marcação de tags HTML.&lt;/p&gt;

&lt;h2 id="entendendo-os-dados"&gt;Entendendo os Dados&lt;/h2&gt;

&lt;p&gt;Neste tutorial, iremos trabalhar com dados do site oficial do &lt;a href="https://www.nga.gov/"&gt;National Gallery of Art&lt;/a&gt; nos Estados Unidos. O National Gallery é um museu de arte localizado no National Mall em Washington, D.C. Ele possui mais de 120.000 peças datadas desde o Renascimento aos dias atuais feitas por mais de 13.000 artistas. &lt;/p&gt;

&lt;p&gt;Gostaríamos de pesquisar o Índice de Artistas, que, no momento da atualização deste tutorial, estava disponível via &lt;a href="https://archive.org/"&gt;Internet Archive’s&lt;/a&gt; &lt;a href="https://web.archive.org/"&gt;Wayback Machine&lt;/a&gt; na seguinte URL:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://web.archive.org/web/20170131230332/https://www.nga.gov/collection/an.shtm"&gt;&lt;strong&gt;https://web.archive.org/web/20170131230332/https://www.nga.gov/collection/an.shtm&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;span class='note'&gt;&lt;p&gt;
&lt;strong&gt;Nota:&lt;/strong&gt; A longa URL acima é devido a este site ter sido arquivado pelo Internet Archive.&lt;/p&gt;

&lt;p&gt;O Internet Archive é uma biblioteca digital sem fins lucrativos que fornece acesso livre a sites da internet e outras mídias digitais. A organização tira instantâneos de websites para preservar a história dos sites, e atualmente, podemos acessar uma versão mais antiga do site da National Gallery que estava disponível quando este tutorial foi escrito pela primeira vez. O Internet Archive é uma boa ferramenta para se ter em mente sempre que estiver fazendo qualquer tipo de scraping de dados históricos, incluindo a comparação entre iterações do mesmo site e dados disponíveis.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;&lt;/span&gt;

&lt;p&gt;Logo abaixo do cabeçalho do Internet Archive, você verá uma página como esta:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/eng_python/beautiful-soup/index-of-artists-landing-page.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Como estamos fazendo esse projeto para aprender sobre o web scraping com o Beautiful Soup, não precisamos extrair muitos dados do site, por isso, vamos limitar o escopo dos dados do artista que estamos tentando capturar. Vamos, portanto, escolher uma letra — em nosso exemplo escolheremos a letra &lt;strong&gt;Z&lt;/strong&gt; — e veremos uma página como esta:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/eng_python/beautiful-soup/artist-names-beginning-with-z-2018.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Na página acima, vemos que o primeiro artista listado no momento da escrita é &lt;strong&gt;Zabaglia, Niccola&lt;/strong&gt;, o que é uma boa coisa a se notar quando começamos a extrair dados. Começaremos trabalhando com essa primeira página, com a seguinte URL para a letra &lt;strong&gt;Z&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://web.archive.org/web/20121007172955/http://www.nga.gov/collection/anZ1.htm"&gt;&lt;strong&gt;https://web.archive.org/web/20121007172955/http://www.nga.gov/collection/an&lt;span class="highlight"&gt;Z&lt;/span&gt;1.htm&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;É importante observar, para análise posterior, quantas páginas existem para a letra que você está escolhendo listar, o que você pode descobrir clicando na última página de artistas. Nesse caso, existe um total de 4 páginas, e o último artista listado no momento da escrita é &lt;strong&gt;Zykmund, Václav&lt;/strong&gt;. A última página de artistas com &lt;strong&gt;Z&lt;/strong&gt; tem a seguinte URL:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://web.archive.org/web/20121010201041/http://www.nga.gov/collection/anZ4.htm"&gt;&lt;strong&gt;https://web.archive.org/web/20121010201041/http://www.nga.gov/collection/an&lt;span class="highlight"&gt;Z&lt;/span&gt;4.htm&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Contudo&lt;/strong&gt;, você também pode acessar a página acima usando a mesma string numérica do Internet Archive da primeira página:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://web.archive.org/web/20121007172955/http://www.nga.gov/collection/anZ4.htm"&gt;&lt;strong&gt;https://web.archive.org/web/20121007172955/http://www.nga.gov/collection/an&lt;span class="highlight"&gt;Z&lt;/span&gt;4.htm&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;É importante observar isso, pois mais adiante neste tutorial faremos a iteração dessas páginas.&lt;/p&gt;

&lt;p&gt;Para começar a se familiarizar com a forma que essa página web é configurada, você pode dar uma olhada em seu &lt;a href="https://www.digitalocean.com/community/tutorials/introduction-to-the-dom"&gt;DOM&lt;/a&gt;, que o ajudará a entender como o HTML é estruturado. Para inspecionar o DOM, você pode abrir &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-use-the-javascript-developer-console#understanding-other-development-tools"&gt;Ferramentas do Desenvolvedor&lt;/a&gt; do seu navegador.&lt;/p&gt;

&lt;h2 id="importando-as-bibliotecas"&gt;Importando as Bibliotecas&lt;/h2&gt;

&lt;p&gt;Para iniciar nosso projeto de codificação, vamos ativar nosso ambiente de programação. Certifique-se de que você está no diretório onde o seu ambiente de desenvolvimento está localizado, e execute o seguinte comando.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;. &lt;span class="highlight"&gt;my_env&lt;/span&gt;/bin/activate
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Com o nosso ambiente de programação ativado, vamos criar um novo arquivo, com o nano por exemplo. Você pode nomear seu arquivo como quiser, vamos chamá-lo de &lt;code&gt;nga_z_artists.py&lt;/code&gt; nesse tutorial.&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="(my_env)sammy@sammy:~/environment$"&gt;nano &lt;span class="highlight"&gt;nga_z_artists.py&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nesse arquivo, podemos começar a importar as bibliotecas que iremos utilizar — &lt;a href="http://docs.python-requests.org/en/master/"&gt;Requests&lt;/a&gt; e Beautiful Soup.&lt;/p&gt;

&lt;p&gt;A biblioteca Requests lhe permite fazer uso do HTTP dentro dos seus programas Python em um formato legível, e o módulo Beautiful Soup é projetado para fazer web scraping rapidamente.&lt;/p&gt;

&lt;p&gt;Vamos importar tanto o Requests quanto o Beautiful Soup com a &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-import-modules-in-python-3"&gt;declaração &lt;code&gt;import&lt;/code&gt;&lt;/a&gt;. Para o Beautiful Soup iremos importá-lo do &lt;code&gt;bs4&lt;/code&gt;, o pacote no qual o Beautiful Soup 4 é encontrado.&lt;/p&gt;
&lt;div class="code-label " title="nga_z_artists.py"&gt;nga_z_artists.py&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-python"&gt;
# Importar bibliotecas
import requests
from bs4 import BeautifulSoup
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Com os módulos Requests e Beautiful Soup importados, podemos passar a trabalhar para coletar primeiro uma página e analisá-la.&lt;/p&gt;

&lt;h2 id="coletando-e-analisando-uma-página-web"&gt;Coletando e Analisando uma Página Web&lt;/h2&gt;

&lt;p&gt;O próximo passo que precisaremos fazer é coletar a URL da primeira página web com o Requests. Iremos atribuir a URL da primeira página à &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-use-variables-in-python-3"&gt;variável&lt;/a&gt; &lt;code&gt;page&lt;/code&gt; usando o &lt;a href="http://docs.python-requests.org/en/master/user/quickstart/#make-a-request"&gt;método &lt;code&gt;requests.get()&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;div class="code-label " title="nga_z_artists.py"&gt;nga_z_artists.py&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-python"&gt;
import requests
from bs4 import BeautifulSoup


# Coletar a primeira página da lista de artistas
&lt;span class="highlight"&gt;page = requests.get('https://web.archive.org/web/20121007172955/https://www.nga.gov/collection/anZ1.htm')&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span class='note'&gt;&lt;strong&gt;Nota:&lt;/strong&gt; Como a URL é longa, o código acima, bem como todo este tutorial não passarão no &lt;a href="https://www.python.org/dev/peps/pep-0008/#maximum-line-length"&gt;PEP 8 E501&lt;/a&gt;, que sinaliza linhas com mais de 79 caracteres. Você pode querer atribuir a URL a uma variável para tornar o código mais legível nas versões finais. O código neste tutorial é para fins de demonstração e permitirá que você troque URLs mais curtas como parte de seus próprios projetos.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;Agora iremos criar o objeto &lt;code&gt;BeautifulSoup&lt;/code&gt;, ou uma árvore de análise. Esse objeto utiliza como argumento o documento &lt;code&gt;page.text&lt;/code&gt; do Requests (o conteúdo da resposta do servidor) e então o analisa através do &lt;a href="https://docs.python.org/3/library/html.parser.html"&gt;&lt;code&gt;html.parser&lt;/code&gt;&lt;/a&gt; interno do Python.&lt;/p&gt;
&lt;div class="code-label " title="nga_z_artists.py"&gt;nga_z_artists.py&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-python"&gt;
import requests
from bs4 import BeautifulSoup


page = requests.get(&lt;span class="highlight"&gt;'https://web.archive.org/web/20121007172955/https://www.nga.gov/collection/anZ1.htm'&lt;/span&gt;)

# Criar o objeto BeautifulSoup
&lt;span class="highlight"&gt;soup = BeautifulSoup(page.text, 'html.parser')&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Com a nossa página coletada, analisada e configurada como um objeto &lt;code&gt;BeautifulSoup&lt;/code&gt;, podemos passar para a coleta dos dados que gostaríamos.&lt;/p&gt;

&lt;h2 id="pegando-texto-de-uma-página-web"&gt;Pegando Texto de uma Página Web&lt;/h2&gt;

&lt;p&gt;Para este projeto, iremos coletar nomes de artistas e os links relevantes disponíveis no website. Você pode querer coletar dados diferentes, tais como a nacionalidade dos artistas e datas. Para quaisquer dados que você queira coletar, você precisa descobrir como ele é descrito pelo DOM da página web.&lt;/p&gt;

&lt;p&gt;Para fazer isso, no seu navegador web, clique com o botão direito — ou &lt;code&gt;CTRL&lt;/code&gt; + clique no macOS — no nome do primeiro artista, &lt;strong&gt;Zabaglia, Niccola&lt;/strong&gt;. Dentro do menu de contexto que aparece, você deve ver um item de menu semelhante ao &lt;strong&gt;Inspecionar Elemento&lt;/strong&gt; (Firefox) ou &lt;strong&gt;Inspecionar&lt;/strong&gt; (Chrome).&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/eng_python/beautiful-soup/inspect-element.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Após clicar no item de menu relevante &lt;strong&gt;Inspecionar&lt;/strong&gt;, as ferramentas para desenvolvedores web devem aparecer no seu navegador. Queremos procurar pela classe e as tags associadas aos nomes dos artistas nessa lista. &lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/eng_python/beautiful-soup/web-page-inspector.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Veremos primeiro que a tabela de nomes está dentro de tags &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; onde &lt;code&gt;class="BodyText"&lt;/code&gt;. É importante observar isso, para que só procuremos texto nessa seção da página web. Também notamos que o nome &lt;strong&gt;Zabaglia, Niccola&lt;/strong&gt; está em uma tag de link, já que o nome faz referência a uma página web que descreve o artista. Então, vamos querer referenciar a tag &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; para links. O nome de cada artista é uma referência a um link.&lt;/p&gt;

&lt;p&gt;Para fazer isso, iremos utilizar os métodos &lt;code&gt;find()&lt;/code&gt; e &lt;code&gt;find_all()&lt;/code&gt; do Beautiful Soup a fim de extrair o texto dos nomes dos artistas do &lt;code&gt;BodyText&lt;/code&gt; &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;div class="code-label " title="nga_z_artists.py"&gt;nga_z_artists.py&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-python"&gt;
import requests
from bs4 import BeautifulSoup


# Coletar e analisar a primeira página
page = requests.get(&lt;span class="highlight"&gt;'https://web.archive.org/web/20121007172955/https://www.nga.gov/collection/anZ1.htm'&lt;/span&gt;)
soup = BeautifulSoup(page.text, &lt;span class="highlight"&gt;'html.parser'&lt;/span&gt;)

# Pegar todo o texto da div BodyText
&lt;span class="highlight"&gt;artist_name_list = soup.find(class_='BodyText')&lt;/span&gt;

# Pegar o texto de todas as instâncias da tag &amp;lt;a&amp;gt; dentro da div BodyText
&lt;span class="highlight"&gt;artist_name_list_items = artist_name_list.find_all('a')&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A seguir, na parte inferior do nosso arquivo de programa, criaremos um &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-construct-for-loops-in-python-3"&gt;loop &lt;code&gt;for&lt;/code&gt;&lt;/a&gt; para iterar todos os nomes de artistas que acabamos de colocar na variável &lt;code&gt;artist_name_list_items&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Vamos imprimir esses nomes com o método &lt;code&gt;prettify()&lt;/code&gt; para transformar a árvore de análise do Beautiful Soup em uma string Unicode bem formatada.  &lt;/p&gt;
&lt;div class="code-label " title="nga_z_artists.py"&gt;nga_z_artists.py&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-python"&gt;
...
artist_name_list = soup.find(class_=&lt;span class="highlight"&gt;'BodyText'&lt;/span&gt;)
artist_name_list_items = artist_name_list.find_all(&lt;span class="highlight"&gt;'a'&lt;/span&gt;)

# Criar loop para imprimir todos os nomes de artistas
&lt;span class="highlight"&gt;for artist_name in artist_name_list_items:&lt;/span&gt;
    &lt;span class="highlight"&gt;print(artist_name.prettify())&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Vamos executar o programa como ele está até agora:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="(my_env)sammy@sammy:~/environment$"&gt;python nga_z_artists.py
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Assim que fizermos isso, receberemos a seguinte saída:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;&amp;lt;a href="/web/20121007172955/https://www.nga.gov/cgi-bin/tsearch?artistid=11630"&amp;gt;
 Zabaglia, Niccola
&amp;lt;/a&amp;gt;
...
&amp;lt;a href="/web/20121007172955/https://www.nga.gov/cgi-bin/tsearch?artistid=3427"&amp;gt;
 Zao Wou-Ki
&amp;lt;/a&amp;gt;
&amp;lt;a href="/web/20121007172955/https://www.nga.gov/collection/anZ2.htm"&amp;gt;
 Zas-Zie
&amp;lt;/a&amp;gt;

&amp;lt;a href="/web/20121007172955/https://www.nga.gov/collection/anZ3.htm"&amp;gt;
 Zie-Zor
&amp;lt;/a&amp;gt;

&amp;lt;a href="/web/20121007172955/https://www.nga.gov/collection/anZ4.htm"&amp;gt;
 &amp;lt;strong&amp;gt;
  next
  &amp;lt;br/&amp;gt;
  page
 &amp;lt;/strong&amp;gt;
&amp;lt;/a&amp;gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;O que vemos na saída nesse ponto é o texto completo e as tags relativas a todos os nomes de artistas dentro de tags &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; encontradas na tag &lt;code&gt;&amp;lt;div class="BodyText"&amp;gt;&lt;/code&gt; na primeira página, bem como algum texto de link adicional na parte inferior. Como não queremos essa informação extra, vamos trabalhar para remover isso na próxima seção. &lt;/p&gt;

&lt;h2 id="removendo-dados-supérfluos"&gt;Removendo Dados Supérfluos&lt;/h2&gt;

&lt;p&gt;Até agora, conseguimos coletar todos os dados de texto do link dentro de uma seção &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; da nossa página web. No entanto, não queremos ter os links inferiores que não fazem referência aos nomes dos artistas. Por isso, vamos trabalhar para remover essa parte.&lt;/p&gt;

&lt;p&gt;Para remover os links inferiores da página, vamos clicar novamente com o botão direito e &lt;strong&gt;Inspecionar&lt;/strong&gt; o DOM. Veremos que os links na parte inferior da seção &lt;code&gt;&amp;lt;div class="BodyText"&amp;gt;&lt;/code&gt; estão contidos em uma tabela HTML: &lt;code&gt;&amp;lt;table class="AlphaNav"&amp;gt;&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/eng_python/beautiful-soup/html-table.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Podemos, portanto, usar o Beautiful Soup para encontrar a classe &lt;code&gt;AlphaNav&lt;/code&gt; e usar o método &lt;code&gt;decompose()&lt;/code&gt; para remover uma tag da árvore de análise e depois destruí-la juntamente com seu conteúdo.&lt;/p&gt;

&lt;p&gt;Usaremos a variável &lt;code&gt;last_links&lt;/code&gt; para fazer referência a esses links inferiores e adicioná-los ao arquivo do programa:&lt;/p&gt;
&lt;div class="code-label " title="nga_z_artists.py"&gt;nga_z_artists.py&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-python"&gt;
import requests
from bs4 import BeautifulSoup


page = requests.get(&lt;span class="highlight"&gt;'https://web.archive.org/web/20121007172955/https://www.nga.gov/collection/anZ1.htm'&lt;/span&gt;)

soup = BeautifulSoup(page.text, &lt;span class="highlight"&gt;'html.parser'&lt;/span&gt;)

# Remover links inferiores
&lt;span class="highlight"&gt;last_links = soup.find(class_='AlphaNav')&lt;/span&gt;
&lt;span class="highlight"&gt;last_links.decompose()&lt;/span&gt;

artist_name_list = soup.find(class_='BodyText')
artist_name_list_items = artist_name_list.find_all('a')

for artist_name in artist_name_list_items:
    print(artist_name.prettify())

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Agora, quando executarmos o programa com o comando &lt;code&gt;python nga_z_artist.py&lt;/code&gt;, receberemos a seguinte saída:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;&amp;lt;a href="/web/20121007172955/https://www.nga.gov/cgi-bin/tsearch?artistid=11630"&amp;gt;
 Zabaglia, Niccola
&amp;lt;/a&amp;gt;
&amp;lt;a href="/web/20121007172955/https://www.nga.gov/cgi-bin/tsearch?artistid=34202"&amp;gt;
 Zaccone, Fabian
&amp;lt;/a&amp;gt;
...
&amp;lt;a href="/web/20121007172955/http://www.nga.gov/cgi-bin/tsearch?artistid=11631"&amp;gt;
 Zanotti, Giampietro
&amp;lt;/a&amp;gt;
&amp;lt;a href="/web/20121007172955/http://www.nga.gov/cgi-bin/tsearch?artistid=3427"&amp;gt;
 Zao Wou-Ki
&amp;lt;/a&amp;gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nesse ponto, vemos que a saída não inclui mais os links na parte inferior da página web e agora exibe apenas os links associados aos nomes dos artistas.&lt;/p&gt;

&lt;p&gt;Até agora, focamos especificamente os links com os nomes dos artistas, mas temos os dados de tags extras que realmente não queremos. Vamos remover isso na próxima seção.&lt;/p&gt;

&lt;h2 id="pegando-o-conteúdo-de-uma-tag"&gt;Pegando o Conteúdo de uma Tag&lt;/h2&gt;

&lt;p&gt;Para acessar apenas os nomes reais dos artistas, queremos focar no conteúdo das tags &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; em vez de imprimir toda a tag de link. &lt;/p&gt;

&lt;p&gt;Podemos fazer isso com o &lt;code&gt;.contents&lt;/code&gt; do Beautiful Soup, que irá retornar a tag filha com um &lt;a href="https://www.digitalocean.com/community/tutorials/understanding-lists-in-python-3"&gt;tipo de dados lista&lt;/a&gt; do Python.&lt;/p&gt;

&lt;p&gt;Vamos revisar o loop &lt;code&gt;for&lt;/code&gt; para que, em vez de imprimir o link inteiro e sua tag, façamos a impressão da lista das filhas (ou seja, os nomes completos dos artistas).&lt;/p&gt;
&lt;div class="code-label " title="nga_z_artists.py"&gt;nga_z_artists.py&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-python"&gt;
import requests
from bs4 import BeautifulSoup


page = requests.get(&lt;span class="highlight"&gt;'https://web.archive.org/web/20121007172955/https://www.nga.gov/collection/anZ1.htm'&lt;/span&gt;)

soup = BeautifulSoup(page.text, &lt;span class="highlight"&gt;'html.parser'&lt;/span&gt;)

last_links = soup.find(class_=&lt;span class="highlight"&gt;'AlphaNav'&lt;/span&gt;)
last_links.decompose()

artist_name_list = soup.find(class_=&lt;span class="highlight"&gt;'BodyText'&lt;/span&gt;)
artist_name_list_items = artist_name_list.find_all(&lt;span class="highlight"&gt;'a'&lt;/span&gt;)

# Usar .contents para pegar as tags &amp;lt;a&amp;gt; filhas
for artist_name in artist_name_list_items:
    &lt;span class="highlight"&gt;names = artist_name.contents[0]&lt;/span&gt;
    &lt;span class="highlight"&gt;print(names)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note que estamos iterando na lista acima chamando o &lt;a href="https://www.digitalocean.com/community/tutorials/understanding-lists-in-python-3#indexing-lists"&gt;número do índice&lt;/a&gt; de cada item.&lt;/p&gt;

&lt;p&gt;Podemos executar o programa com o comando &lt;code&gt;python&lt;/code&gt; para ver a seguinte saída:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Zabaglia, Niccola
Zaccone, Fabian
Zadkine, Ossip
...
Zanini-Viola, Giuseppe
Zanotti, Giampietro
Zao Wou-Ki

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Recebemos de volta uma lista de todos os nomes dos artistas disponíveis na primeira página da letra &lt;strong&gt;Z&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Mas, e se quisermos também capturar as URLs associadas a esses artistas? Podemos extrair URLs encontradas dentro de tags &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; utilizando o método &lt;code&gt;get('href')&lt;/code&gt; do Beautiful Soup. &lt;/p&gt;

&lt;p&gt;A partir da saída dos links acima, sabemos que a URL inteira não está sendo capturada, então vamos &lt;a href="https://www.digitalocean.com/community/tutorials/an-introduction-to-working-with-strings-in-python-3#string-concatenation"&gt;concatenar&lt;/a&gt; a string do link com o início da string da URL (nesse caso &lt;code&gt;https://web.archive.org/&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Estas linhas também serão adicionadas ao loop &lt;code&gt;for&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-label " title="nga_z_artists.py"&gt;nga_z_artists.py&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-python"&gt;
...
for artist_name in artist_name_list_items:
    names = artist_name.contents[0]
    &lt;span class="highlight"&gt;links = 'https://web.archive.org' + artist_name.get('href')&lt;/span&gt;
    print(names)
    &lt;span class="highlight"&gt;print(links)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Quando executamos o programa acima, receberemos tanto os nomes dos artistas quanto as URLs para os links que nos dizem mais sobre eles:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Zabaglia, Niccola
https://web.archive.org/web/20121007172955/https://www.nga.gov/cgi-bin/tsearch?artistid=11630
Zaccone, Fabian
https://web.archive.org/web/20121007172955/https://www.nga.gov/cgi-bin/tsearch?artistid=34202
...
Zanotti, Giampietro
https://web.archive.org/web/20121007172955/https://www.nga.gov/cgi-bin/tsearch?artistid=11631
Zao Wou-Ki
https://web.archive.org/web/20121007172955/https://www.nga.gov/cgi-bin/tsearch?artistid=3427

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Embora estejamos agora recebendo informações do site, ele está apenas imprimindo em nossa janela de terminal. Em vez disso, vamos capturar esses dados para podermos usá-los em outro lugar, gravando-os em um arquivo. &lt;/p&gt;

&lt;h2 id="gravando-os-dados-em-um-arquivo-csv"&gt;Gravando os Dados em um Arquivo CSV&lt;/h2&gt;

&lt;p&gt;Coletar dados que só residem em uma janela de terminal não é muito útil. Arquivos de valores separados por vírgulas (CSV) nos permitem armazenar dados tabulares em texto plano, e é um formato comum para planilhas e bancos de dados. Antes de iniciar esta seção, você deve familiarizar-se com &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-handle-plain-text-files-in-python-3"&gt;como manipular arquivos de texto sem formatação em Python&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Primeiro, precisamos importar o módulo interno &lt;code&gt;csv&lt;/code&gt; do Python junto com os outros módulos no topo do arquivo de código: &lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-python"&gt;import csv
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Em seguida, vamos criar e abrir um arquivo chamado &lt;code&gt;&lt;span class="highlight"&gt;z-artist-names&lt;/span&gt;.csv&lt;/code&gt; para que possamos &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-handle-plain-text-files-in-python-3#step-4-%E2%80%94-writing-a-file"&gt;gravar nele&lt;/a&gt; (iremos utilizar aqui a variável &lt;code&gt;f&lt;/code&gt; para o arquivo), utilizando o modo &lt;code&gt;'w'&lt;/code&gt;. Também vamos escrever os cabeçalhos da primeira linha: &lt;code&gt;Name&lt;/code&gt; and &lt;code&gt;Link&lt;/code&gt; que iremos passar para o método &lt;code&gt;writerow()&lt;/code&gt; como uma lista.&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-python"&gt;f = csv.writer(open(&lt;span class="highlight"&gt;'z-artist-names.csv', 'w'&lt;/span&gt;))
f.writerow(&lt;span class="highlight"&gt;['Name', 'Link']&lt;/span&gt;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finalmente, dentro do nosso loop &lt;code&gt;for&lt;/code&gt;, vamos escrever cada linha com os &lt;code&gt;names&lt;/code&gt; ou nomes dos artistas e seus &lt;code&gt;links&lt;/code&gt; associados: &lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-python"&gt;f.writerow([names, links])
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Você pode ver as linhas para cada uma dessas tarefas no arquivo abaixo:&lt;/p&gt;
&lt;div class="code-label " title="nga_z_artists.py"&gt;nga_z_artists.py&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-python"&gt;
import requests
&lt;span class="highlight"&gt;import csv&lt;/span&gt;
from bs4 import BeautifulSoup


page = requests.get(&lt;span class="highlight"&gt;'https://web.archive.org/web/20121007172955/http://www.nga.gov/collection/anZ1.htm'&lt;/span&gt;)

soup = BeautifulSoup(page.text, &lt;span class="highlight"&gt;'html.parser'&lt;/span&gt;)

last_links = soup.find(class_=&lt;span class="highlight"&gt;'AlphaNav'&lt;/span&gt;)
last_links.decompose()

# Criar um arquivo para gravar, adicionar linha de cabeçalhos
&lt;span class="highlight"&gt;f = csv.writer(open('z-artist-names.csv', 'w'))&lt;/span&gt;
&lt;span class="highlight"&gt;f.writerow(['Name', 'Link'])&lt;/span&gt;

artist_name_list = soup.find(class_=&lt;span class="highlight"&gt;'BodyText'&lt;/span&gt;)
artist_name_list_items = artist_name_list.find_all(&lt;span class="highlight"&gt;'a'&lt;/span&gt;)

for artist_name in artist_name_list_items:
    names = artist_name.contents[0]
    links = &lt;span class="highlight"&gt;'https://web.archive.org'&lt;/span&gt; + artist_name.get(&lt;span class="highlight"&gt;'href'&lt;/span&gt;)


    # Adicionar em uma linha o nome de cada artista e o link associado
    &lt;span class="highlight"&gt;f.writerow([names, links])&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Quando você executar o programa agora com o comando &lt;code&gt;python&lt;/code&gt;, nenhuma saída será retornada para sua janela de terminal. Em vez disso, um arquivo será criado no diretório em que você está trabalhando, chamado &lt;code&gt;&lt;span class="highlight"&gt;z-artist-names&lt;/span&gt;.csv&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Dependendo do que você usa para abrí-lo, ele deve ser algo assim:&lt;/p&gt;
&lt;div class="code-label " title="z-artist-names.csv"&gt;z-artist-names.csv&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;
Name,Link
"Zabaglia, Niccola",https://web.archive.org/web/20121007172955/http://www.nga.gov/cgi-bin/tsearch?artistid=11630
"Zaccone, Fabian",https://web.archive.org/web/20121007172955/http://www.nga.gov/cgi-bin/tsearch?artistid=34202
"Zadkine, Ossip",https://web.archive.org/web/20121007172955/http://www.nga.gov/cgi-bin/tsearch?artistid=3475w
...

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ou, ele pode se parecer mais com uma planilha:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/eng_python/beautiful-soup/csv-spreadsheet-2018.png" alt=""&gt;&lt;/p&gt;

&lt;p&gt;Em ambos os casos, agora você pode usar esse arquivo para trabalhar com os dados de maneiras mais significativas, já que as informações coletadas agora estão armazenadas no disco do seu computador.&lt;/p&gt;

&lt;h2 id="recuperando-páginas-relacionadas"&gt;Recuperando Páginas Relacionadas&lt;/h2&gt;

&lt;p&gt;Criamos um programa que extrairá dados da primeira página da lista de artistas cujos sobrenomes começam com a letra &lt;strong&gt;Z&lt;/strong&gt;. Porém, existem 4 páginas desses artistas no total, disponíveis no website.&lt;/p&gt;

&lt;p&gt;Para coletar todas essas páginas, podemos executar mais iterações com loops &lt;code&gt;for&lt;/code&gt;. Isso revisará a maior parte do código que escrevemos até agora, mas empregará conceitos semelhantes.&lt;/p&gt;

&lt;p&gt;Para começar, vamos inicializar uma lista para manter as páginas:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-python"&gt;pages = []
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Vamos preencher essa lista inicializada com o seguinte loop &lt;code&gt;for&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-python"&gt;for i in range(1, 5):
    url = &lt;span class="highlight"&gt;'https://web.archive.org/web/20121007172955/https://www.nga.gov/collection/anZ'&lt;/span&gt; + str(i) + &lt;span class="highlight"&gt;'.htm'&lt;/span&gt;
    pages.append(url)

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href="https://www.digitalocean.com/community/tutorials/how-to-scrape-web-pages-with-beautiful-soup-and-python-3#understanding-the-data"&gt;Anteriormente neste tutorial&lt;/a&gt;, observamos que devemos prestar atenção ao número total de páginas que contêm nomes de artistas começando com a letra &lt;strong&gt;Z&lt;/strong&gt; (ou qualquer letra que estivermos utilizando). Uma vez que existem 4 páginas para a letra &lt;strong&gt;Z&lt;/strong&gt;, construímos o loop &lt;code&gt;for&lt;/code&gt;acima com um intervalo de &lt;code&gt;1&lt;/code&gt; a &lt;code&gt;5&lt;/code&gt; de modo que ele vai iterar através de cada uma das 4 páginas.&lt;/p&gt;

&lt;p&gt;Para este website específico, as URLs começam com a string &lt;code&gt;https://web.archive.org/web/20121007172955/https://www.nga.gov/collection/anZ&lt;/code&gt;e são seguidas com um número de página (que será o inteiro &lt;code&gt;i&lt;/code&gt; do loop &lt;code&gt;for&lt;/code&gt; que &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-convert-data-types-in-python-3"&gt;convertemos para uma string&lt;/a&gt;) e terminam com &lt;code&gt;.htm&lt;/code&gt;. Iremos concatenar estas strings e depois acrescentar o resultado à lista &lt;code&gt;pages&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Além desse loop, teremos um segundo loop que passará por cada uma das páginas acima. O código nesse loop &lt;code&gt;for&lt;/code&gt; será parecido com o código que criamos até agora, já que ele está executando a tarefa que completamos para a primeira página dos artistas com a letra &lt;strong&gt;Z&lt;/strong&gt; para cada um das 4 páginas do total. Observe que, como colocamos o programa original no segundo loop &lt;code&gt;for&lt;/code&gt;, agora temos o loop original como um &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-construct-for-loops-in-python-3#nested-for-loops"&gt;loop &lt;code&gt;for&lt;/code&gt; aninhado&lt;/a&gt; contido nele.&lt;/p&gt;

&lt;p&gt;Os dois loops &lt;code&gt;for&lt;/code&gt; ficarão assim:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-python"&gt;pages = &lt;span class="highlight"&gt;[]&lt;/span&gt;

for i in range(1, 5):
    &lt;span class="highlight"&gt;url = 'https://web.archive.org/web/20121007172955/https://www.nga.gov/collection/anZ'&lt;/span&gt; + str(i) + &lt;span class="highlight"&gt;'.htm'&lt;/span&gt;
    pages.append(url)

for item in pages:
    page = requests.get(item)
    soup = BeautifulSoup(page.text, &lt;span class="highlight"&gt;'html.parser'&lt;/span&gt;)

    last_links = soup.find(class_=&lt;span class="highlight"&gt;'AlphaNav'&lt;/span&gt;)
    last_links.decompose()

    artist_name_list = soup.find(class_=&lt;span class="highlight"&gt;'BodyText'&lt;/span&gt;)
    artist_name_list_items = artist_name_list.find_all(&lt;span class="highlight"&gt;'a'&lt;/span&gt;)

    for artist_name in artist_name_list_items:
        names = artist_name.contents&lt;span class="highlight"&gt;[0]&lt;/span&gt;
        links = &lt;span class="highlight"&gt;'https://web.archive.org'&lt;/span&gt; + artist_name.get(&lt;span class="highlight"&gt;'href'&lt;/span&gt;)

        f.writerow([names, links])

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;No código acima, você deve ver que o primeiro loop &lt;code&gt;for&lt;/code&gt; está iterando nas páginas e o segundo loop &lt;code&gt;for&lt;/code&gt; está extraindo dados de cada uma dessas páginas e, em seguida, adicionando os nomes e links dos artistas, linha por linha, em cada linha de cada página.&lt;/p&gt;

&lt;p&gt;Estes dois loops &lt;code&gt;for&lt;/code&gt; estão abaixo das declaraçõs &lt;code&gt;import&lt;/code&gt;, da criação e escrita do arquivo CSV (com a linha para a escrita dos cabeçalhos do arquivo), e a inicialização da variável &lt;code&gt;pages&lt;/code&gt; (atribuída a uma lista).&lt;/p&gt;

&lt;p&gt;Dentro de um contexto macro do arquivo de programação, o código completo se parece com isto:&lt;/p&gt;
&lt;div class="code-label " title="nga_z_artists.py"&gt;nga_z_artists.py&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-python"&gt;
import requests
import csv
from bs4 import BeautifulSoup


f = csv.writer(open(&lt;span class="highlight"&gt;'z-artist-names.csv'&lt;/span&gt;, &lt;span class="highlight"&gt;'w'&lt;/span&gt;))
f.writerow(&lt;span class="highlight"&gt;['Name', 'Link']&lt;/span&gt;)

pages = &lt;span class="highlight"&gt;[]&lt;/span&gt;

for i in range(1, 5):
    url = &lt;span class="highlight"&gt;'https://web.archive.org/web/20121007172955/https://www.nga.gov/collection/anZ'&lt;/span&gt; + str(i) + &lt;span class="highlight"&gt;'.htm'&lt;/span&gt;
    pages.append(url)


for item in pages:
    page = requests.get(item)
    soup = BeautifulSoup(page.text, &lt;span class="highlight"&gt;'html.parser'&lt;/span&gt;)

    last_links = soup.find(class_=&lt;span class="highlight"&gt;'AlphaNav'&lt;/span&gt;)
    last_links.decompose()

    artist_name_list = soup.find(class_=&lt;span class="highlight"&gt;'BodyText'&lt;/span&gt;)
    artist_name_list_items = artist_name_list.find_all(&lt;span class="highlight"&gt;'a'&lt;/span&gt;)

    for artist_name in artist_name_list_items:
        names = artist_name.contents[0]
        links = &lt;span class="highlight"&gt;'https://web.archive.org'&lt;/span&gt; + artist_name.get(&lt;span class="highlight"&gt;'href'&lt;/span&gt;)

        f.writerow([names, links])


&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Como esse programa está fazendo um trabalho, levará algum tempo para criar o arquivo CSV. Depois de concluído, a saída estará comleta, mostrando os nomes dos artistas e seus links associados de &lt;strong&gt;Zabaglia, Niccola&lt;/strong&gt; até &lt;strong&gt;Zykmund, Václav&lt;/strong&gt;.&lt;/p&gt;

&lt;h2 id="sendo-cuidadoso"&gt;Sendo Cuidadoso&lt;/h2&gt;

&lt;p&gt;Ao fazer scraping em páginas web, é importante manter-se cuidadoso com os servidores dos quais você está pegando informações. &lt;/p&gt;

&lt;p&gt;Verifique se o site tem termos de serviço ou termos de uso relacionados ao web scraping. Além disso, verifique se o site tem uma API que permite coletar dados antes de você mesmo fazer scraping.&lt;/p&gt;

&lt;p&gt;Certifique-se de não acessar continuamente os servidores para coletar dados. Depois de coletar o que você precisa de um site, execute scripts que vasculhem pelos dados localmente, em vez de sobrecarregar os servidores de outra pessoa.&lt;/p&gt;

&lt;p&gt;Adicionalmente, é uma boa ideia fazer web scraping com um cabeçalho que tenha o seu nome e e-mail para que o website possa identificá-lo e fazer o acompanhamento caso tenha alguma dúvida. Um exemplo de cabeçalho que você pode usar com a biblioteca Requests do Python é o seguinte:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-python"&gt;import requests

headers = {
    &lt;span class="highlight"&gt;'User-Agent': 'Seu nome, example.com',&lt;/span&gt;
    &lt;span class="highlight"&gt;'From': 'email@example.com'&lt;/span&gt;
}

url = &lt;span class="highlight"&gt;'https://example.com'&lt;/span&gt;

page = requests.get(url, headers = headers)

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A utilização de cabeçalhos com informações identificáveis ​​garante que as pessoas que acessam os logs de um servidor possam entrar em contato com você.&lt;/p&gt;

&lt;h2 id="conclusão"&gt;Conclusão&lt;/h2&gt;

&lt;p&gt;Este tutorial usou o Python e o Beautiful Soup para coletar dados de um website. Armazenamos o texto que reunimos em um arquivo CSV.&lt;/p&gt;

&lt;p&gt;Você pode continuar trabalhando neste projeto coletando mais dados e tornando seu arquivo CSV mais robusto. Por exemplo, você pode querer incluir as nacionalidades e os anos de cada artista. Você também pode usar o que aprendeu para coletar dados de outros sites.&lt;/p&gt;

&lt;p&gt;Para continuar aprendendo sobre como extrair informações da web, leia nosso tutorial “&lt;a href="https://www.digitalocean.com/community/tutorials/how-to-crawl-a-web-page-with-scrapy-and-python-3"&gt;How To Crawl A Web Page with Scrapy and Python 3.&lt;/a&gt;”&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-install-software-on-kubernetes-clusters-with-the-helm-package-manager</id>
    <published>2018-08-09T18:30:35Z</published>
    <updated>2018-09-19T19:10:12Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-install-software-on-kubernetes-clusters-with-the-helm-package-manager"/>
    <title>How To Install Software on Kubernetes Clusters with the Helm Package Manager</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.helm.sh"&gt;Helm&lt;/a&gt; is a package manager for Kubernetes that allows developers and operators to more easily configure and deploy applications on Kubernetes clusters.&lt;/p&gt;

&lt;p&gt;In this tutorial we will set up Helm and use it to install, reconfigure, rollback, then delete an instance of &lt;a href="https://github.com/kubernetes/dashboard"&gt;the Kubernetes Dashboard application&lt;/a&gt;. The dashboard is an official web-based Kubernetes GUI.&lt;/p&gt;

&lt;p&gt;For a conceptual overview of Helm and its packaging ecosystem, please read our article &lt;a href="https://www.digitalocean.com/community/tutorials/an-introduction-to-helm-the-package-manager-for-kubernetes"&gt;&lt;em&gt;An Introduction to Helm&lt;/em&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;For this tutorial you will need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  A Kubernetes 1.8+ cluster with role-based access control (RBAC) enabled.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;code&gt;kubectl&lt;/code&gt; command-line tool installed on your local machine, configured to connect to your cluster. You can read more about installing &lt;code&gt;kubectl&lt;/code&gt; &lt;a href="https://kubernetes.io/docs/tasks/tools/install-kubectl/"&gt;in the official documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can test your connectivity with the following command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl cluster-info
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you see no errors, you're connected to the cluster. If you access multiple clusters with &lt;code&gt;kubectl&lt;/code&gt;, be sure to verify that you've selected the correct cluster context:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl config get-contexts
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;CURRENT   NAME                    CLUSTER                      AUTHINFO                      NAMESPACE
&lt;span class="highlight"&gt;*&lt;/span&gt;         do-nyc1-k8s-example     do-nyc1-k8s-example          do-nyc1-k8s-example-admin
          docker-for-desktop      docker-for-desktop-cluster   docker-for-desktop
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example the asterisk (&lt;code&gt;&lt;span class="highlight"&gt;*&lt;/span&gt;&lt;/code&gt;) indicates that we are connected to the &lt;code&gt;do-nyc1-k8s-example&lt;/code&gt; cluster. To switch clusters run:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl config use-context &lt;span class="highlight"&gt;context-name&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When you are connected to the correct cluster, continue to Step 1 to begin installing Helm.&lt;/p&gt;

&lt;h2 id="step-1-—-installing-helm"&gt;Step 1 — Installing Helm&lt;/h2&gt;

&lt;p&gt;First we'll install the &lt;code&gt;helm&lt;/code&gt; command-line utility on our local machine. Helm provides a script that handles the installation process on MacOS, Windows, or Linux.&lt;/p&gt;

&lt;p&gt;Change to a writable directory and download the script from Helm's GitHub repository:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cd /tmp
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;curl https://raw.githubusercontent.com/kubernetes/helm/master/scripts/get &amp;gt; install-helm.sh
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Make the script executable with &lt;code&gt;chmod&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;chmod u+x install-helm.sh
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At this point you can use your favorite text editor to open the script and inspect it to make sure it’s safe. When you are satisfied, run it:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;./install-helm.sh
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You may be prompted for your password. Provide it and press &lt;code&gt;ENTER&lt;/code&gt;.&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;helm installed into /usr/local/bin/helm
Run 'helm init' to configure helm.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next we will finish the installation by installing some Helm components on our cluster.&lt;/p&gt;

&lt;h2 id="step-2-—-installing-tiller"&gt;Step 2 — Installing Tiller&lt;/h2&gt;

&lt;p&gt;Tiller is a companion to the &lt;code&gt;helm&lt;/code&gt; command that runs on your cluster, receiving commands from &lt;code&gt;helm&lt;/code&gt; and communicating directly with the Kubernetes API to do the actual work of creating and deleting resources. To give Tiller the permissions it needs to run on the cluster, we are going to make a Kubernetes &lt;code&gt;serviceaccount&lt;/code&gt; resource.&lt;/p&gt;

&lt;span class='note'&gt;&lt;p&gt;
&lt;strong&gt;Note:&lt;/strong&gt; We will bind this &lt;code&gt;serviceaccount&lt;/code&gt; to the &lt;strong&gt;cluster-admin&lt;/strong&gt; cluster role. This will give the &lt;code&gt;tiller&lt;/code&gt; service superuser access to the cluster and allow it to install all resource types in all namespaces. This is fine for exploring Helm, but you may want a more locked-down configuration for a production Kubernetes cluster.&lt;/p&gt;

&lt;p&gt;Please refer to &lt;a href="https://docs.helm.sh/using_helm/#role-based-access-control"&gt;the official Helm RBAC documentation&lt;/a&gt; for more information on setting up different RBAC scenarios for Tiller.&lt;br&gt;&lt;/p&gt;&lt;/span&gt;

&lt;p&gt;Create the &lt;strong&gt;tiller&lt;/strong&gt; &lt;code&gt;serviceaccount&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl -n kube-system create serviceaccount tiller
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, bind the &lt;strong&gt;tiller&lt;/strong&gt; &lt;code&gt;serviceaccount&lt;/code&gt; to the &lt;strong&gt;cluster-admin&lt;/strong&gt; role:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl create clusterrolebinding tiller --clusterrole cluster-admin --serviceaccount=kube-system:tiller
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we can run &lt;code&gt;helm init&lt;/code&gt;, which installs Tiller on our cluster, along with some local housekeeping tasks such as downloading the &lt;strong&gt;stable&lt;/strong&gt; repo details:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;helm init --service-account tiller
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;. . .

Tiller (the Helm server-side component) has been installed into your Kubernetes Cluster.

Please note: by default, Tiller is deployed with an insecure 'allow unauthenticated users' policy.
For more information on securing your installation see: https://docs.helm.sh/using_helm/#securing-your-helm-installation
Happy Helming!
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To verify that Tiller is running, list the pods in the&lt;strong&gt;kube-system&lt;/strong&gt; namespace:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl get pods --namespace kube-system
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;NAME                                    READY     STATUS    RESTARTS   AGE
. . .
kube-dns-64f766c69c-rm9tz               3/3       Running   0          22m
kube-proxy-worker-5884                  1/1       Running   1          21m
kube-proxy-worker-5885                  1/1       Running   1          21m
kubernetes-dashboard-7dd4fc69c8-c4gwk   1/1       Running   0          22m
&lt;span class="highlight"&gt;tiller-deploy-5c688d5f9b-lccsk          1/1       Running   0          40s&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The Tiller pod name begins with the prefix &lt;code&gt;tiller-deploy-&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now that we've installed both Helm components, we're ready to use &lt;code&gt;helm&lt;/code&gt; to install our first application.&lt;/p&gt;

&lt;h2 id="step-3-—-installing-a-helm-chart"&gt;Step 3 — Installing a Helm Chart&lt;/h2&gt;

&lt;p&gt;Helm software packages are called &lt;em&gt;charts&lt;/em&gt;. Helm comes preconfigured with a curated chart repository called &lt;strong&gt;stable&lt;/strong&gt;. You can browse the available charts &lt;a href="https://github.com/helm/charts/tree/master/stable"&gt;in their GitHub repo&lt;/a&gt;. We are going to install the &lt;a href="https://github.com/kubernetes/dashboard"&gt;Kubernetes Dashboard&lt;/a&gt; as an example.&lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;helm&lt;/code&gt; to install the &lt;code&gt;kubernetes-dashboard&lt;/code&gt; package from the &lt;code&gt;stable&lt;/code&gt; repo:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;helm install stable/kubernetes-dashboard --name dashboard-demo
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;NAME:   &lt;span class="highlight"&gt;dashboard-demo&lt;/span&gt;
LAST DEPLOYED: Wed Aug  8 20:11:07 2018
NAMESPACE: default
STATUS: DEPLOYED

. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notice the &lt;code&gt;NAME&lt;/code&gt; line, highlighted in the above example output. In this case we specified the name &lt;code&gt;dashboard-demo&lt;/code&gt;. This is the name of our &lt;em&gt;release&lt;/em&gt;. A Helm &lt;em&gt;release&lt;/em&gt; is a single deployment of one chart with a specific configuration. You can deploy multiple releases of the same chart with, each with its own configuration.&lt;/p&gt;

&lt;p&gt;If you don't specify your own release name using &lt;code&gt;--name&lt;/code&gt;, Helm will create a random name for you.&lt;/p&gt;

&lt;p&gt;We can ask Helm for a list of releases on this cluster:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;helm list
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;NAME            REVISION    UPDATED                     STATUS      CHART                       NAMESPACE
&lt;span class="highlight"&gt;dashboard-demo&lt;/span&gt;    1           Wed Aug  8 20:11:11 2018    DEPLOYED    kubernetes-dashboard-0.7.1  default
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can now use &lt;code&gt;kubectl&lt;/code&gt; to verify that a new service has been deployed on the cluster:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl get services
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;NAME                                   TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
&lt;span class="highlight"&gt;dashboard-demo-kubernetes-dashboard   ClusterIP   10.32.104.73   &amp;lt;none&amp;gt;        443/TCP   51s&lt;/span&gt;
kubernetes                             ClusterIP   10.32.0.1      &amp;lt;none&amp;gt;        443/TCP   34m
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notice that by default the service name corresponding to our release is a combination of the Helm release name and the chart name.&lt;/p&gt;

&lt;p&gt;Now that we've deployed the application, let's use Helm to change its configuration and update the deployment.&lt;/p&gt;

&lt;h2 id="step-4-—-updating-a-release"&gt;Step 4 — Updating a Release&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;helm upgrade&lt;/code&gt; command can be used to upgrade a release with a new or updated chart, or update the it’s configuration options.&lt;/p&gt;

&lt;p&gt;We're going to make a simple change to our &lt;code&gt;dashboard-demo&lt;/code&gt; release to demonstrate the update and rollback process: we'll update the name of the dashboard service to just &lt;code&gt;dashboard&lt;/code&gt;, instead of &lt;code&gt;dashboard-demo-kubernetes-dashboard&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;kubernetes-dashboard&lt;/code&gt; chart provides a &lt;code&gt;fullnameOverride&lt;/code&gt; configuration option to control the service name. Let's run &lt;code&gt;helm upgrade&lt;/code&gt; with this option set:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;helm upgrade &lt;span class="highlight"&gt;dashboard-demo&lt;/span&gt; stable/kubernetes-dashboard &lt;span class="highlight"&gt;--set fullnameOverride="dashboard"&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You'll see output similar to the initial &lt;code&gt;helm install&lt;/code&gt; step.&lt;/p&gt;

&lt;p&gt;Check if your Kubernetes services reflect the updated values:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl get services
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;NAME                   TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
kubernetes             ClusterIP   10.32.0.1       &amp;lt;none&amp;gt;        443/TCP   36m
&lt;span class="highlight"&gt;dashboard&lt;/span&gt;              ClusterIP   10.32.198.148   &amp;lt;none&amp;gt;        443/TCP   40s
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Our service name has been updated to the new value.&lt;/p&gt;

&lt;span class='note'&gt;&lt;p&gt;
&lt;strong&gt;Note:&lt;/strong&gt; At this point you may want to actually load the Kubernetes Dashboard in your browser and check it out. To do so, first run the following command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl proxy
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This creates a proxy that lets you access remote cluster resources from your local computer. Based on the previous instructions your dashboard service is named &lt;code&gt;kubernetes-dashboard&lt;/code&gt; and it's running in the &lt;code&gt;default&lt;/code&gt; namespace. You may now access the dashboard at the following url:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;http://localhost:8001/api/v1/namespaces/&lt;span class="highlight"&gt;default&lt;/span&gt;/services/https:&lt;span class="highlight"&gt;dashboard&lt;/span&gt;:/proxy/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If necessary, substitute your own service name and namespace for the highlighted portions. Instructions for actually using the dashboard are out of scope for this tutorial, but you can read &lt;a href="https://github.com/kubernetes/dashboard/wiki"&gt;the official Kubernetes Dashboard docs&lt;/a&gt; for more information.&lt;br&gt;&lt;/p&gt;&lt;/span&gt;

&lt;p&gt;Next we'll look at Helm's ability to roll back releases.&lt;/p&gt;

&lt;h2 id="step-5-—-rolling-back-a-release"&gt;Step 5 — Rolling Back a Release&lt;/h2&gt;

&lt;p&gt;When we updated our &lt;code&gt;&lt;span class="highlight"&gt;dashboard-demo&lt;/span&gt;&lt;/code&gt; release in the previous step, we created a second &lt;em&gt;revision&lt;/em&gt; of the release. Helm retains all the details of previous releases in case you need to roll back to a prior configuration or chart.&lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;helm list&lt;/code&gt; to inspect the release again:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;helm list
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;NAME            REVISION    UPDATED                     STATUS      CHART                       NAMESPACE
dashboard-demo  &lt;span class="highlight"&gt;2&lt;/span&gt;         Wed Aug  8 20:13:15 2018    DEPLOYED    kubernetes-dashboard-0.7.1  default
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;REVISION&lt;/code&gt; column tells us that this is now the second revision.&lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;helm rollback&lt;/code&gt; to roll back to the first revision:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;helm rollback &lt;span class="highlight"&gt;dashboard-demo&lt;/span&gt; 1
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see the following output, indicating that the rollback succeeded:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Rollback was a success! Happy Helming!
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At this point, if you run &lt;code&gt;kubectl get services&lt;/code&gt; again, you will notice that the service name has changed back to its previous value. Helm has re-deployed the application with revision 1's configuration.&lt;/p&gt;

&lt;p&gt;Next we'll look into deleting releases with Helm.&lt;/p&gt;

&lt;h2 id="step-6-—-deleting-a-release"&gt;Step 6 — Deleting a Release&lt;/h2&gt;

&lt;p&gt;Helm releases can be deleted with the &lt;code&gt;helm delete&lt;/code&gt; command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;helm delete &lt;span class="highlight"&gt;dashboard-demo&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;release "dashboard-demo" deleted
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Though the release has been deleted and the dashboard application is no longer running, Helm saves all the revision information in case you want to re-deploy the release. If you tried to &lt;code&gt;helm install&lt;/code&gt; a new &lt;code&gt;dashboard-demo&lt;/code&gt; release right now, you'd get an error:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;Error: a release named dashboard-demo already exists.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you use the &lt;code&gt;--deleted&lt;/code&gt; flag to list your deleted releases, you'll see that the release is still around:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;helm list --deleted
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;NAME            REVISION    UPDATED                     STATUS  CHART                       NAMESPACE
dashboard-demo  3           Wed Aug  8 20:15:21 2018    DELETED kubernetes-dashboard-0.7.1  default
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To &lt;em&gt;really&lt;/em&gt; delete the release and purge all old revisions, use the &lt;code&gt;--purge&lt;/code&gt; flag with the &lt;code&gt;helm delete&lt;/code&gt; command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;helm delete dashboard-demo --purge
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now the release has been truly deleted, and you can reuse the release name.&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;In this tutorial we installed the &lt;code&gt;helm&lt;/code&gt; command-line tool and its &lt;code&gt;tiller&lt;/code&gt; companion service. We also explored installing, upgrading, rolling back, and deleting Helm charts and releases.&lt;/p&gt;

&lt;p&gt;For more information about Helm and Helm charts, please see &lt;a href="https://docs.helm.sh/"&gt;the official Helm documentation&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/arquitetando-aplicacoes-para-o-kubernetes-pt</id>
    <published>2018-08-15T18:44:13Z</published>
    <updated>2018-08-15T18:44:24Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/arquitetando-aplicacoes-para-o-kubernetes-pt"/>
    <title>Arquitetando Aplicações para o Kubernetes</title>
    <content type="html">&lt;h3 id="introdução"&gt;Introdução&lt;/h3&gt;

&lt;p&gt;Projetar e executar aplicações com escalabilidade, portabilidade e robustez pode ser um desafio, especialmente à medida que a complexidade do sistema aumenta. A arquitetura de uma aplicação ou sistema impacta significativamente em como ele deve ser executado, o que ele espera de seu ambiente e o quanto está acoplado aos componentes relacionados. Seguir certos padrões durante a fase de projeto e aderir a certas práticas operacionais pode ajudar a combater alguns dos problemas mais comuns que as aplicações enfrentam ao executar em ambientes altamente distribuídos.&lt;/p&gt;

&lt;p&gt;Embora os padrões de projeto de software e as metodologias de desenvolvimento possam produzir aplicações com as características corretas de escalabilidade, a infraestrutura e o ambiente influenciam a operação do sistema implantado. Tecnologias como o &lt;a href="https://www.docker.com/"&gt;Docker&lt;/a&gt; e o &lt;a href="https://kubernetes.io/"&gt;Kubernetes&lt;/a&gt; ajudam equipes a empacotar software e depois distribuir, fazer o deploy e escalar em plataformas de computadores distribuídos. Aprender a aproveitar melhor o poder dessas ferramentas pode ajudá-lo a gerenciar aplicações com maior flexibilidade, controle e capacidade de resposta.&lt;/p&gt;

&lt;p&gt;Neste guia, vamos discutir alguns dos princípios e padrões que você pode adotar para ajudá-lo a dimensionar e gerenciar suas cargas de trabalho no Kubernetes. Embora o Kubernetes possa executar muitos tipos de cargas de trabalho, as escolhas feitas podem afetar a facilidade de operação e as possibilidades disponíveis no deploy. A maneira como você projeta e constrói suas aplicações, empacota seus serviços em containers e configura o gerenciamento do ciclo de vida e o comportamento dentro do Kubernetes pode, cada uma, influenciar sua experiência.&lt;/p&gt;

&lt;h2 id="projetando-para-escalabilidade-das-aplicações"&gt;Projetando para Escalabilidade das Aplicações&lt;/h2&gt;

&lt;p&gt;Ao produzir software, muitos requisitos afetam os padrões e a arquitetura que você escolhe empregar. Com o Kubernetes, um dos fatores mais importantes é a capacidade de &lt;strong&gt;escalar horizontalmente&lt;/strong&gt;, ajustando o número de cópias idênticas da sua aplicação para distribuir a carga e aumentar a disponibilidade. Esta é uma alternativa à &lt;strong&gt;escalabilidade vertical&lt;/strong&gt;, que tenta manipular os mesmos fatores através do deploy em máquinas com maiores ou menores recursos.&lt;/p&gt;

&lt;p&gt;Em particular, &lt;strong&gt;microservices&lt;/strong&gt; ou micro-serviços é um padrão de projeto de software que funciona bem para deployments escaláveis em clusters. Os desenvolvedores criam aplicações pequenas e compostas que se comunicam pela rede através de APIs REST bem definidas, em vez de grandes programas compostos que se comunicam através de mecanismos internos de programação. A decomposição de aplicações monolíticas em componentes discretos de propósito único torna possível dimensionar cada função de forma independente. Grande parte da complexidade e composição que normalmente existiria no nível da aplicação é transferida para o domínio operacional, onde pode ser gerenciada por plataformas como o Kubernetes.&lt;/p&gt;

&lt;p&gt;Além dos padrões de software específicos, as aplicações &lt;strong&gt;cloud native&lt;/strong&gt; ou nativas para nuvem são projetadas com algumas considerações adicionais em mente. Aplicações &lt;em&gt;cloud native&lt;/em&gt; são programas que seguem um padrão de arquitetura de microservices com resiliência integrada, observabilidade e recursos administrativos para se adaptar ao ambiente fornecido por plataformas em cluster na nuvem.&lt;/p&gt;

&lt;p&gt;Por exemplo, aplicações &lt;em&gt;cloud native&lt;/em&gt; são construídas com métricas de relatórios de integridade para permitir que a plataforma gerencie eventos do ciclo de vida se uma instância se tornar inconsistente. Eles produzem (e tornam disponível para exportação) dados robustos de telemetria para alertar os operadores sobre problemas e permitir que eles tomem decisões esclarecidas. As aplicações são projetadas para lidar com reinicializações e falhas regulares, alterações na disponibilidade do back-end e alta carga, sem corromper os dados ou deixar de responder.&lt;/p&gt;

&lt;h3 id="seguindo-a-filosofia-12-factor-application"&gt;Seguindo a Filosofia 12 Factor Application&lt;/h3&gt;

&lt;p&gt;Uma metodologia popular que pode ajudá-lo a se concentrar nas características mais importantes ao criar aplicações web prontas para a nuvem é a filosofia &lt;a href="https://12factor.net/"&gt;Twelve-Factor App&lt;/a&gt;. Escritos para ajudar os desenvolvedores e as equipes de operações a entender as principais qualidades compartilhadas pelos web services projetados para serem executados na nuvem, os princípios se aplicam muito bem ao software que funcionará em um ambiente em cluster como o Kubernetes. Embora os aplicativos monolíticos possam se beneficiar seguindo estas recomendações, as arquiteturas de microservices projetadas em torno desses princípios funcionam particularmente bem.&lt;/p&gt;

&lt;p&gt;Um breve sumário dos Doze Fatores são:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Base de código:&lt;/strong&gt; Gerencie todo o código em sistemas de controle de versão (como Git ou Mercurial). A base de código determina de forma abrangente o que é implantado.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Dependências:&lt;/strong&gt; As dependências devem ser gerenciadas completa e explicitamente pela base de código, seja armazenada com o código ou com a versão fixada em um formato no qual um gerenciador de pacotes possa instalar.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Configuração:&lt;/strong&gt; Separe os parâmetros de configuração da aplicação e defina-os no ambiente de deployment, em vez de mantê-los dentro da própria aplicação.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Serviços de apoio:&lt;/strong&gt; Os serviços locais e remotos são abstraídos como recursos acessíveis pela rede, com detalhes de conexão definidos na configuração.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Construa, libere, execute:&lt;/strong&gt; O estágio de construção da sua aplicação deve ser completamente separado dos processos de liberação e operação da mesma. O estágio de construção cria um artefato de deployment a partir do código-fonte, o estágio de liberação combina o artefato e a configuração, e o estágio de execução executa o release.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Processos:&lt;/strong&gt; Aplicações são implementadas como processos que não devem contar com o armazenamento de estado localmente. O estado deve ser transferido para um serviço de apoio, conforme descrito no quarto fator.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ligações à portas:&lt;/strong&gt; As aplicações devem ligar-se nativamente a uma porta e ouvir conexões. Roteamento e encaminhamento de solicitações devem ser tratados externamente.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Concorrência:&lt;/strong&gt; As aplicações devem confiar na escalabilidade através do modelo de processo. A execução de várias cópias de uma aplicação simultaneamente, potencialmente em vários servidores, permite o escalonamento sem ajustar o código da aplicação.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Descartabilidade:&lt;/strong&gt; Os processos devem ser capazes de iniciar rapidamente e parar normalmente sem efeitos colaterais sérios.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Paridade Desenv/prod:&lt;/strong&gt; Seus ambientes de teste, preparação e produção devem ter uma correspondência próxima e ser mantidos em sincronia. Diferenças entre ambientes são oportunidades para incompatibilidades e configurações não testadas aparecerem.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Logs:&lt;/strong&gt; As aplicações devem transmitir os logs para a saída padrão, para que os serviços externos possam decidir a melhor forma de lidar com eles.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Processos administrativos:&lt;/strong&gt; Processos de administração únicos devem ser executados em releases específicos e enviados com o código do processo principal.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Ao aderir às diretrizes fornecidas pelos Doze Fatores, você pode criar e executar aplicativos usando um modelo adequado ao ambiente de execução do Kubernetes. Os Doze Fatores encorajam os desenvolvedores a se concentrarem na responsabilidade principal de suas aplicações, a considerarem as condições operacionais e as interfaces entre os componentes e a usarem entradas, saídas e recursos de gerenciamento de processos padrão, para serem executados de maneira previsível no Kubernetes.&lt;/p&gt;

&lt;h2 id="containerizando-componentes-da-aplicação"&gt;Containerizando Componentes da Aplicação&lt;/h2&gt;

&lt;p&gt;O Kubernetes usa containers para executar aplicações empacotadas isoladas em seus nodes de cluster. Para executar no Kubernetes, suas aplicações devem ser encapsuladas em uma ou mais imagens de container e executadas usando um runtime de container como o Docker. Embora containerizar seus componentes seja um requisito para o Kubernetes, isso também ajuda a reforçar muitos dos princípios da metodologia de doze fatores para aplicações, discutidos acima, permitindo fácil escalonamento e gerenciamento.&lt;/p&gt;

&lt;p&gt;Por exemplo, os containers fornecem isolamento entre o ambiente da aplicação e o sistema do host externo, suporta uma abordagem em rede, orientada a serviços para comunicação entre aplicações, e normalmente busca a configuração através de variáveis de ambiente e expõe logs gravados na saída de erro e na saída padrão. Os próprios containers encorajam a concorrência baseada em processos e ajudam a manter a paridade desenv/prod sendo escaláveis independentemente e agrupando o ambiente de runtime do processo. Essas características tornam possível empacotar suas aplicações para que elas funcionem sem problemas no Kubernetes.&lt;/p&gt;

&lt;h3 id="diretrizes-para-otimizar-containers"&gt;Diretrizes para Otimizar Containers&lt;/h3&gt;

&lt;p&gt;A flexibilidade da tecnologia de container permite muitas maneiras diferentes de encapsular uma aplicação. Contudo, alguns métodos funcionam melhor do que outros em um ambiente Kubernetes.&lt;/p&gt;

&lt;p&gt;A maioria das boas práticas recomendadas na containerização de suas aplicações tem a ver com a construção de imagens, onde você define como seu software será configurado e executado dentro de um container. Em geral, manter tamanhos de imagem pequenos e compostos oferece vários benefícios. Imagens de tamanho otimizado podem reduzir o tempo e os recursos necessários para iniciar um novo container em um cluster, mantendo o perfil gerenciável e reutilizando as camadas existentes entre as atualizações de imagem.&lt;/p&gt;

&lt;p&gt;Um bom primeiro passo ao criar imagens de container é fazer o seu melhor para separar suas etapas de criação da imagem final que será executada na produção. Construir software geralmente requer ferramental extra, toma tempo adicional e produz artefatos que podem ser inconsistentes de container para container ou desnecessários para o runtime de execução final, dependendo do ambiente. Uma maneira de separar claramente o processo de construção do runtime de execução é usar as &lt;a href="https://docs.docker.com/develop/develop-images/multistage-build/"&gt;construções de vários estágios do Docker&lt;/a&gt;. As configurações das construções de vários estágios permitem que você especifique uma imagem de base para usar durante o processo de construção e defina outra para usar no runtime. Isso possibilita a criação de software usando uma imagem com todas as ferramentas de compilação instaladas e copiar os artefatos resultantes para uma imagem simplificada que será usada a cada vez, posteriormente.&lt;/p&gt;

&lt;p&gt;Com esse tipo de funcionalidade disponível, geralmente é uma boa ideia criar imagens de produção em cima de uma imagem matriz mínima. Se você quiser evitar completamente o inchaço encontrado em camadas da imagem matriz do estilo "distro" como &lt;code&gt;ubuntu:16.04&lt;/code&gt; (que inclui um ambiente Ubuntu 16.04 bastante completo), você pode construir suas imagens com o &lt;code&gt;scratch&lt;/code&gt; — A imagem de base mais minimalista do Docker — como matriz. Contudo, a camada base &lt;code&gt;scratch&lt;/code&gt; não fornece acesso a muitas das ferramentas principais e muitas vezes quebrará as suposições sobre o ambiente que alguns softwares detêm. Como uma alternativa, a imagem &lt;code&gt;alpine&lt;/code&gt; &lt;a href="https://alpinelinux.org/"&gt;Alpine Linux&lt;/a&gt; ganhou poularidade por ser um ambiente de base sólido e mínimo, que fornece uma distribuição Linux minúscula, mas com recursos completos.  &lt;/p&gt;

&lt;p&gt;Para linguagens interpretadas como o Python ou Ruby, o paradigma muda levemente, já que não há um estágio de compilação e o interpretador deve estar disponível para executar o código em produção. No entanto, como as imagens magras ainda são ideais, muitas imagens otimizadas para linguagens específicas, construídas a partir do Alpine Linux estão disponíveis no &lt;a href="https://hub.docker.com/"&gt;Docker Hub&lt;/a&gt;. Os benefícios de se usar uma imagem menor para linguagens interpretadas são semelhantes aos das linguagens compiladas: O Kubernetes poderá buscar rapidamente todas as imagens de container necessárias para novos nodes para começar a fazer trabalho significativo.&lt;/p&gt;

&lt;h2 id="decidindo-sobre-o-escopo-para-containers-e-pods"&gt;Decidindo sobre o Escopo para Containers e Pods&lt;/h2&gt;

&lt;p&gt;Embora suas aplicações devam ser containerizadas para executar em um cluster Kubernetes, os &lt;strong&gt;pods&lt;/strong&gt; são a menor unidade de abstração que o Kubernetes pode gerenciar diretamente. Um pod é um objeto Kubernetes composto de um ou mais containers fortemente acoplados. os containers em um pod compartilham um ciclo de vida e são gerenciados juntos como uma única unidade. Por exemplo, o scheduling desses containers é feito sempre no mesmo node, são iniciados e parados em sincronia e compartilham recursos como sistemas de arquivos e espaço de IP.  &lt;/p&gt;

&lt;p&gt;No início, pode ser difícil descobrir a melhor maneira de dividir suas aplicações em containers e pods. Isso torna importante entender como o Kubernetes lida com esses componentes e o que cada camada de abstração fornece para seus sistemas. Algumas considerações podem ajudá-lo a identificar alguns pontos naturais de encapsulamento para sua aplicação com cada uma dessas abstrações.&lt;/p&gt;

&lt;p&gt;Uma maneira de determinar um escopo efetivo para seus containers é procurar por limites naturais do desenvolvimento. Se seus sistemas operam utilizando a arquitetura de microservices, containers bem projetados são frequentemente construídos para representar unidades discretas de funcionalidade que podem ser usadas em uma variedade de contextos. Esse nível de abstração permite à sua equipe lançar alterações nas imagens de containers e, em seguida, fazer o deploy dessa nova funcionalidade em qualquer ambiente onde essas imagens sejam utilizadas. As aplicações podem ser construídas através da composição de containers individuais que preencham uma determinada função, mas podem não realizar um processo inteiro sozinhos.&lt;/p&gt;

&lt;p&gt;Em contraste com o exposto cima, os pods são geralmente construídos pensando em quais partes do seu sistema podem se beneficiar mais do gerenciamento independente. Como o Kubernetes usa pods como sua menor abstração de interação com o usuário, essas são as unidades mais primitivas com as quais as ferramentas e a API do Kubernetes podem interagir e controlar diretamente. Você pode iniciar, parar e reiniciar pods, ou utilizar objetos de nível superior construídos em pods para introduzir recursos de replicação e gerenciamento do ciclo e vida. O Kubernetes não lhe permite gerenciar os containers dentro de um pod de forma independente, portanto, você não deve agrupar containers que possam se beneficiar de uma administração separada.&lt;/p&gt;

&lt;p&gt;Como muitos dos recursos e abstrações do Kubernetes lidam diretamente com os pods, faz sentido agrupar itens que devem ser escalados juntos em um único pod e separar aqueles que devem ser escalados independentemente. Por exemplo, separar seus servidores web de seus servidores de aplicação em diferentes pods permite escalar cada camada de forma independente, conforme necessário. No entanto, o empacotamento de um servidor web e de um adaptador de banco de dados no mesmo conjunto pode fazer sentido se o adaptador fornecer a funcionalidade essencial que o servidor web precisa para funcionar corretamente.&lt;/p&gt;

&lt;h3 id="melhorando-a-funcionalidade-do-pod-através-do-empacotamento-de-containers-de-apoio"&gt;Melhorando a Funcionalidade do Pod através do Empacotamento de Containers de Apoio&lt;/h3&gt;

&lt;p&gt;Com isso em mente, quais tipos de containers devem ser agrupados em um único pod? Geralmente, um container primário é responsável pelo cumprimento das funções principais do pod, mas podem ser definidos containers adicionais que modifiquem ou estendam o container principal ou o ajudem a se conectar a um ambiente de deployment exclusivo.&lt;/p&gt;

&lt;p&gt;Por exemplo, em um pod de servidor web, um container Nginx pode escutar solicitações e fornecer conteúdo enquanto um container associado atualiza arquivos estáticos quando um repositório é alterado. Pode ser tentador empacotar esses dois componentes em um único container, mas há benefícios significativos em implementá-los como containers separados. O container do servidor web e o atualizador do repositório podem ser usados independentemente em diferentes contextos. Eles podem ser mantidos por equipes diferentes e cada um deles podem ser desenvolvidos para generalizar seu comportamento para trabalhar com diferentes containers complementares.&lt;/p&gt;

&lt;p&gt;Brendan Burns e David Oppenheimer identificaram três padrões primários para empacotar containers de apoio em seus artigos em &lt;a href="https://static.googleusercontent.com/media/research.google.com/en//pubs/archive/45406.pdf"&gt;design patterns for container-based distributed systems&lt;/a&gt;. Estes representam alguns dos casos de uso mais comuns para empacotamento de containers juntos em um pod:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Sidecar:&lt;/strong&gt; Neste padrão, o container secundário estende e aprimora a funcionalidade principal do container primário. Esse padrão envolve a execução de funções não padronizadas ou utilitárias em um container separado. Por exemplo, um container que encaminha logs ou monitora atualizações em valores de configuração pode aumentar a funcionalidade de um pod sem alterar significativamente seu foco principal.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ambassador:&lt;/strong&gt; O padrão ambassador usa um container suplementar para abstrair recursos remotos para o container principal. O container principal se conecta diretamente ao container ambassador que, por sua vez, se conecta e abstrai os pools de recursos externos potencialmente complexos, como um cluster de Redis distribuído. O container principal não precisa saber ou se preocupar com o ambiente de deployment real para se conectar a serviços externos.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Adaptor:&lt;/strong&gt; O padrão adaptor é usado para converter os dados, protocolos ou interfaces do container primário para alinhar com os padrões esperados por terceiros. Containers Adaptor permitem o acesso uniforme a serviços centralizados, mesmo quando as aplicações que eles atendem podem suportar nativamente somente interfaces incompatíveis.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="extraindo-a-configuração-em-configmaps-e-secrets"&gt;Extraindo a Configuração em ConfigMaps e Secrets&lt;/h2&gt;

&lt;p&gt;Embora a configuração da aplicação possa ser posta em imagens de container, é melhor tornar seus componentes configuráveis no runtime para suportar deployments em vários contextos e permitir uma administração mais flexível. Para gerenciar parâmetros de configuração de runtime, o Kubernetes usa dois objetos chamados &lt;strong&gt;ConfigMaps&lt;/strong&gt; e &lt;strong&gt;Secrets&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;ConfigMaps é um mecanismo usado para armazenar dados que podem ser expostos a pods e outros objetos em runtime. Os dados armazenados dentro de ConfigMaps podem ser apresentados como variáveis de ambiente ou montados como arquivos no pod. Ao projetar suas aplicações para ler esses locais, você pode injetar a configuração no runtime usando o ConfigMaps e modificar o comportamento de seus componentes sem precisar reconstruir a imagem do container.&lt;/p&gt;

&lt;p&gt;Secrets são um tipo similar de objeto Kubernetes usado para armazenar dados sigilosos com segurança e seletivamente permitir o acesso a pods e outros componentes conforme necessário. Os secrets são uma maneira conveniente de transmitir material confidencial para aplicações sem armazená-los como texto simples em locais facilmente acessíveis em sua configuração normal. Funcionalmente, eles trabalham da mesma maneira que os ConfigMaps, portanto, as aplicações podem consumir dados de ConfigMaps e Secrets usando os mesmos mecanismos.&lt;/p&gt;

&lt;p&gt;ConfigMaps e Secrets o ajudam a evitar colocar a configuração diretamente nas definições de objeto do Kubernetes. Você pode mapear a chave de configuração em vez do valor, permitindo que você atualize a configuração dinamicamente, através da modificação do ConfigMap ou do Secret. Isso lhe dá a oportunidade de alterar o comportamento do runtime ativo dos pods e outros objetos do Kubernetes sem modificar as definições dos recursos do Kubernetes.&lt;/p&gt;

&lt;h2 id="implementando-as-provas-readiness-e-liveness"&gt;Implementando as provas Readiness e Liveness&lt;/h2&gt;

&lt;p&gt;O Kubernetes inclui uma grande quantidade de funcionalidades prontas para uso para gerenciar ciclos de vida e garantir que suas aplicações estejam sempre íntegras e disponíveis. No entanto, para aproveitar esses recursos, o Kubernetes precisa entender como ele deve monitorar e interpretar a integridade da sua aplicação. Para fazer isto, o Kubernetes lhe permite definir provas &lt;strong&gt;liveness&lt;/strong&gt; e &lt;strong&gt;readiness&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Provas Liveness permitem que o Kubernetes determine se uma aplicação em um container está no ar e funcionando ativamente. O Kubernetes pode executar comandos periodicamente dentro do container para verificar o comportamento básico da aplicação ou pode enviar solicitações de rede HTTP ou TCP para um local designado para determinar se o processo está disponível e é capaz de responder conforme o esperado. Se uma prova liveness falha, o Kubernetes reinicia o container para tentar restabelecer a funcionalidade dentro do pod.&lt;/p&gt;

&lt;p&gt;Provas Readness são um ferramenta similar usada para determinar se um pod está pronto para servir o tráfego. As aplicações em um container podem precisar executar procedimentos de inicialização antes de estarem prontas para aceitar solicitações de clientes ou podem precisar ser recarregadas ao serem notificadas sobre uma nova configuração. Quando uma prova readness falha, em vez de reiniciar o container, O Kubernetes pára de enviar temporariamente solicitações ao pod. Isso permite que o pod complete suas rotinas de inicialização ou manutenção sem afetar a integridade do grupo como um todo.&lt;/p&gt;

&lt;p&gt;Ao combinar as provas liveness e readiness, você pode instruir o Kubernetes a reiniciar automaticamente os pods ou removê-los dos grupos de back-end. Configurar sua infraestrutura para aproveitar esses recursos permite que o Kubernetes gerencie a disponibilidade e a integridade de suas aplicações sem trabalho operacional adicional.&lt;/p&gt;

&lt;h2 id="usando-deployments-para-gerenciar-escalabilidade-e-disponibilidade"&gt;Usando Deployments para Gerenciar Escalabilidade e Disponibilidade&lt;/h2&gt;

&lt;p&gt;Mais cedo, ao discutir alguns fundamentos de projetos de pods, também mencionamos que outros objetos do Kubernetes são construídos com base nessas primitivas para fornecer funcionalidade mais avançada. Um &lt;strong&gt;deployment&lt;/strong&gt;, um desses objetos compostos, é provavelmente o objeto Kubernetes mais comumente definido e manipulado. &lt;/p&gt;

&lt;p&gt;Deployments são objetos compostos que se baseiam em outras primativas do Kubernetes para somar recursos adicionais. Eles adicionam recursos de gerenciamento de ciclo de vida a objetos intermediários chamados &lt;strong&gt;replicasets&lt;/strong&gt;, como a capacidade de executar atualizações contínuas, rollback para versões anteriores e transição entre estados. Esses replicasets permitem que você defina modelos de pod para lançar e gerenciar várias cópias de um único projeto de pod. Isso ajuda você a escalar facilmente sua infraestrutura, gerenciar os requisitos de disponibilidade e reiniciar automaticamente os pods em caso de falha.&lt;/p&gt;

&lt;p&gt;Esses recursos adicionais fornecem uma estrutura administrativa e recursos de autocorreção para a abstração de pods relativamente simples. Embora os pods sejam as unidades que executam as cargas de trabalho que você define, eles não são as unidades que você geralmente deve provisionar e gerenciar. Em vez disso, pense nos pods como um bloco construtivo que pode executar aplicativos de maneira robusta quando provisionados por meio de objetos de nível mais alto, como os deployments. &lt;/p&gt;

&lt;h2 id="criando-regras-de-serviços-e-de-ingresso-para-gerenciar-o-acesso-às-camadas-de-aplicação"&gt;Criando Regras de Serviços e de Ingresso para Gerenciar o Acesso às Camadas de Aplicação&lt;/h2&gt;

&lt;p&gt;Deployments lhe permitem provisionar e gerenciar conjuntos de pods intercambiáveis para escalar suas aplicações e atender às demandas do usuário. No entanto, o roteamento de tráfego para os pods provisionados é uma preocupação à parte. Como os pods são trocados como parte de atualizações contínuas, reiniciados ou movidos devido a falhas de host, os endereços de rede associados anteriormente ao grupo em execução serão alterados. Os &lt;strong&gt;serviços&lt;/strong&gt; do Kubernetes lhe permitem gerenciar essa complexidade mantendo as informações de roteamento para pools dinâmicos de pods e controlando o acesso a várias camadas da sua infraestrutura.&lt;/p&gt;

&lt;p&gt;No Kubernetes, serviços são mecanismos específicos que controlam como o tráfego é roteado para conjuntos de pods. Seja encaminhando tráfego de clientes externos ou gerenciando conexões entre vários componentes internos, os serviços permitem que você controle como o tráfego deve fluir. O Kubernetes atualizará e manterá todas as informações necessárias para encaminhar conexões para os pods relevantes, mesmo que o ambiente se altere e o cenário de rede mude.&lt;/p&gt;

&lt;h3 id="acessando-serviços-internamente"&gt;Acessando Serviços Internamente&lt;/h3&gt;

&lt;p&gt;Para usar os serviços efetivamente, primeiro você deve determinar os consumidores presumidos para cada grupo de pods. Se o seu serviço só será usado por outras aplicações implantadas em seu cluster do Kubernetes, o tipo de serviço &lt;strong&gt;clusterIP&lt;/strong&gt; permite que você se conecte a um conjunto de pods usando um endereço IP estável que só pode ser roteado de dentro do cluster. Qualquer objeto com deploy no cluster pode se comunicar com o grupo de pods replicados enviando tráfego diretamente para o endereço IP do serviço. Esse é o tipo de serviço mais simples, que funciona bem para camadas internas das aplicações.&lt;/p&gt;

&lt;p&gt;Um add-on opcional de DNS permite que o Kubernetes forneça nomes DNS para serviços. Isso permite que os pods e outros objetos se comuniquem com os serviços pelo nome, em vez do endereço IP. Esse mecanismo não altera significativamente o uso do serviço, mas os identificadores baseados em nome podem simplificar a conexão de componentes ou a definição de interações sem conhecer o endereço IP do serviço antecipadamente.&lt;/p&gt;

&lt;h3 id="expondo-serviços-para-consumo-público"&gt;Expondo Serviços para Consumo Público&lt;/h3&gt;

&lt;p&gt;Se a interface deve ser publicamente acessível, sua melhor opção geralmente é o tipo de serviço &lt;strong&gt;load balancer&lt;/strong&gt;. Ele usa a API do seu provedor de nuvem específico para provisionar um balanceador de carga, que serve tráfego para os pods de serviço por meio de um endereço IP exposto publicamente. Isso lhe permite rotear solicitações externas para os pods em seu serviço, oferecendo um canal de rede controlado para sua rede interna de clusters. &lt;/p&gt;

&lt;p&gt;Como o tipo de serviço load balancer cria um balanceador de carga para cada serviço, pode se tornar caro expor publicamente os serviços do Kubernetes usando esse método. Para ajudar a aliviar isso, os objetos &lt;strong&gt;ingress&lt;/strong&gt; do Kubernetes podem ser usados para descrever como rotear diferentes tipos de solicitações para diferentes serviços com base em um conjunto predeterminado de regras. Por exemplo, solicitações para "example.com" poderiam ir para o serviço A, enquanto solicitações para "sammytheshark.com" poderiam ser roteadas para o serviço B. Os objetos ingress fornecem uma maneira de descrever como rotear logicamente um fluxo misto de solicitações para seus serviços de destino com base em padrões predefinidos. &lt;/p&gt;

&lt;p&gt;Regras Ingress devem ser interpretadas por um &lt;strong&gt;ingress controller&lt;/strong&gt; — normalmente algum tipo de balanceamento de carga, como o Nginx — implantado no cluster como um pod, que implementa as regras de entrada e encaminha o tráfego apropriadamente para os serviços do Kubernetes. Atualmente, o tipo de objeto ingress está na versão beta, mas há várias implementações de trabalho que podem ser usadas para minimizar o número de balanceadores de carga externos que os proprietários de cluster precisam executar.&lt;/p&gt;

&lt;h2 id="usando-sintaxe-declarativa-para-gerenciar-o-estado-do-kubernetes"&gt;Usando Sintaxe Declarativa para Gerenciar o Estado do Kubernetes&lt;/h2&gt;

&lt;p&gt;O Kubernetes oferece bastante flexibilidade na definição e controle dos recursos implantados (com deploy realizado) em seu cluster. Utilizando ferramentas como o &lt;code&gt;kubectl&lt;/code&gt;, você pode definir imperativamente objetos ad-hoc para fazer deploy imediatamente em seu cluster. Embora isso possa ser útil para fazer o deploy de recursos rapidamente ao aprender o Kubernetes, há desvantagens nessa abordagem que a tornam indesejável para a administração de produção a longo prazo.&lt;/p&gt;

&lt;p&gt;Um dos principais problemas com o gerenciamento imperativo é que ele não deixa nenhum registro das alterações que você fez deploy em seu cluster. Isso dificulta ou impossibilita a recuperação no caso de falhas ou para acompanhar as mudanças operacionais à medida que são aplicadas aos seus sistemas.&lt;/p&gt;

&lt;p&gt;Felizmente, o Kubernetes fornece uma sintaxe declarativa alternativa que lhe permite definir recursos em arquivos de texto e em seguida usar o &lt;code&gt;kubectl&lt;/code&gt; para aplicar a configuração ou alteração. Armazenar esses arquivos de configuração em um repositório de controle de versão é uma maneira simples de monitorar alterações e integrar com os processos de revisão usados para outras áreas de sua organização. O gerenciamento baseado em arquivos também simplifica a adaptação de padrões existentes para novos recursos, copiando e editando definições existentes. Armazenar suas definições de objeto do Kubernetes em diretórios versionados permite manter um instantâneo do estado do cluster desejado em cada instante no tempo. Isso pode ter um valor inestimável durante operações de recuperação, migrações ou ao rastrear a causa raiz de alterações não intencionais introduzidas em seu sistema.&lt;/p&gt;

&lt;h2 id="conclusão"&gt;Conclusão&lt;/h2&gt;

&lt;p&gt;Gerenciar a infraestrutura que executará suas aplicações e aprender como aproveitar melhor os recursos oferecidos pelos ambientes modernos de orquestração pode ser assustador. No entanto, muitos dos benefícios oferecidos por sistemas como o Kubernetes e tecnologias como containers ficam mais claros quando suas práticas de desenvolvimento e operações se alinham aos conceitos sobre os quais o ferramental é construído. Arquitetar seus sistemas usando os padrões nos quais o Kubernetes se destaca e entender como certos recursos podem aliviar alguns dos desafios associados a deployments altamente complexos pode ajudar a melhorar sua experiência com a execução na plataforma.  &lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-install-apache-kafka-on-centos-7</id>
    <published>2018-08-03T15:02:10Z</published>
    <updated>2018-08-14T18:33:50Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-install-apache-kafka-on-centos-7"/>
    <title>How To Install Apache Kafka on CentOS 7</title>
    <content type="html">&lt;p&gt;&lt;em&gt;The author selected the &lt;a href="https://www.brightfunds.org/funds/foss-nonprofits"&gt;Free and Open Source Fund&lt;/a&gt; to receive a donation as part of the &lt;a href="https://do.co/w4do-cta"&gt;Write for DOnations&lt;/a&gt; program.&lt;/em&gt;&lt;/p&gt;

&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;&lt;a href="http://kafka.apache.org/"&gt;Apache Kafka&lt;/a&gt; is a popular distributed message broker designed to efficiently handle large volumes of real-time data. A Kafka cluster is not only highly scalable and fault-tolerant, but it also has a much higher throughput compared to other message brokers such as &lt;a href="http://activemq.apache.org/"&gt;ActiveMQ&lt;/a&gt; and &lt;a href="https://www.rabbitmq.com/"&gt;RabbitMQ&lt;/a&gt;. Though it is generally used as a &lt;em&gt;publish/subscribe&lt;/em&gt; messaging system, a lot of organizations also use it for log aggregation because it offers persistent storage for published messages.&lt;/p&gt;

&lt;p&gt;A publish/subscribe messaging system allows one or more producers to publish messages without considering the number of consumers or how they will process the messages. Subscribed clients are notified automatically about updates and the creation of new messages. This system is more efficient and scalable than systems where clients poll periodically to determine if new messages are available.&lt;/p&gt;

&lt;p&gt;In this tutorial, you will install and use Apache Kafka 1.1.0 on CentOS 7.&lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;To follow along, you will need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One CentOS 7 server and a non-root user with sudo privileges. Follow the steps specified in this &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-centos-7"&gt;guide&lt;/a&gt; if you do not have a non-root user set up. &lt;/li&gt;
&lt;li&gt;At least 4GB of RAM on the server. Installations without this amount of RAM may cause the Kafka service to fail, with the &lt;a href="https://en.wikipedia.org/wiki/Java_virtual_machine"&gt;Java virtual machine (JVM)&lt;/a&gt; throwing an "Out Of Memory" exception during startup.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://openjdk.java.net/"&gt;OpenJDK&lt;/a&gt; 8 installed on your server. To install this version, follow &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-java-on-centos-and-fedora#install-openjdk-8"&gt;these instructions&lt;/a&gt; on installing specific versions of OpenJDK. Kafka is written in Java, so it requires a JVM; however, its startup shell script has a version detection bug that causes it to fail to start with JVM versions above 8.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="step-1-—-creating-a-user-for-kafka"&gt;Step 1 — Creating a User for Kafka&lt;/h2&gt;

&lt;p&gt;Since Kafka can handle requests over a network, you should create a dedicated user for it. This minimizes damage to your CentOS machine should the Kafka server be compromised. We will create a dedicated &lt;strong&gt;kafka&lt;/strong&gt; user in this step, but you should create a different non-root user to perform other tasks on this server once you have finished setting up Kafka. &lt;/p&gt;

&lt;p&gt;Logged in as your non-root sudo user, create a user called &lt;strong&gt;kafka&lt;/strong&gt; with the &lt;code&gt;useradd&lt;/code&gt; command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo useradd kafka -m
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;-m&lt;/code&gt; flag ensures that a home directory will be created for the user. This home directory, &lt;code&gt;/home/kafka&lt;/code&gt;, will act as our workspace directory for executing commands in the sections below.&lt;/p&gt;

&lt;p&gt;Set the password using &lt;code&gt;passwd&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo passwd kafka
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add the &lt;strong&gt;kafka&lt;/strong&gt; user to the &lt;code&gt;wheel&lt;/code&gt; group with the &lt;code&gt;adduser&lt;/code&gt; command, so that it has the privileges required to install Kafka's dependencies:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo usermod -aG wheel kafka
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Your &lt;strong&gt;kafka&lt;/strong&gt; user is now ready. Log into this account using &lt;code&gt;su&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;su -l kafka
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that we've created the Kafka-specific user, we can move on to downloading and extracting the Kafka binaries.&lt;/p&gt;

&lt;h2 id="step-2-—-downloading-and-extracting-the-kafka-binaries"&gt;Step 2 — Downloading and Extracting the Kafka Binaries&lt;/h2&gt;

&lt;p&gt;Let's download and extract the Kafka binaries into dedicated folders in our &lt;strong&gt;kafka&lt;/strong&gt; user's home directory.&lt;/p&gt;

&lt;p&gt;To start, create a directory in &lt;code&gt;/home/kafka&lt;/code&gt; called &lt;code&gt;Downloads&lt;/code&gt; to store your downloads:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;mkdir ~/Downloads
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Use &lt;code&gt;curl&lt;/code&gt; to download the Kafka binaries:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;curl "http://www-eu.apache.org/dist/kafka/&lt;span class="highlight"&gt;1.1.0&lt;/span&gt;/kafka_&lt;span class="highlight"&gt;2.12-1.1.0&lt;/span&gt;.tgz" -o ~/Downloads/kafka.tgz
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Create a directory called &lt;code&gt;kafka&lt;/code&gt; and change to this directory. This will be the base directory of the Kafka installation:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;mkdir ~/kafka &amp;amp;&amp;amp; cd ~/kafka
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Extract the archive you downloaded using the &lt;code&gt;tar&lt;/code&gt; command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;tar -xvzf ~/Downloads/kafka.tgz --strip 1
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We specify the &lt;code&gt;--strip 1&lt;/code&gt; flag to ensure that the archive's contents are extracted in &lt;code&gt;~/kafka/&lt;/code&gt; itself and not in another directory (such as &lt;code&gt;~/kafka/kafka_&lt;span class="highlight"&gt;2.12-1.1.0&lt;/span&gt;/&lt;/code&gt;) inside of it.&lt;/p&gt;

&lt;p&gt;Now that we've downloaded and extracted the binaries successfully, we can move on configuring to Kafka to allow for topic deletion. &lt;/p&gt;

&lt;h2 id="step-3-—-configuring-the-kafka-server"&gt;Step 3 — Configuring the Kafka Server&lt;/h2&gt;

&lt;p&gt;Kafka's default behavior will not allow us to delete a &lt;em&gt;topic&lt;/em&gt;, the category, group, or feed name to which messages can be published. To modify this, let's edit the configuration file. &lt;/p&gt;

&lt;p&gt;Kafka's configuration options are specified in &lt;code&gt;server.properties&lt;/code&gt;. Open this file with &lt;code&gt;vi&lt;/code&gt; or your favorite editor:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;vi ~/kafka/config/server.properties
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let's add a setting that will allow us to delete Kafka topics.  Press &lt;code&gt;i&lt;/code&gt; to insert text, and add the following to the bottom of the file:&lt;/p&gt;
&lt;div class="code-label " title="~/kafka/config/server.properties"&gt;~/kafka/config/server.properties&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;delete.topic.enable = true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When you are finished, press &lt;code&gt;ESC&lt;/code&gt; to exit insert mode and &lt;code&gt;:wq&lt;/code&gt; to write the changes to the file and quit. Now that we've configured Kafka, we can move on to creating systemd unit files for running and enabling it on startup. &lt;/p&gt;

&lt;h2 id="step-4-—-creating-systemd-unit-files-and-starting-the-kafka-server"&gt;Step 4 — Creating Systemd Unit Files and Starting the Kafka Server&lt;/h2&gt;

&lt;p&gt;In this section, we will create &lt;a href="https://www.digitalocean.com/community/tutorials/understanding-systemd-units-and-unit-files"&gt;systemd unit files&lt;/a&gt; for the Kafka service. This will help us perform common service actions such as starting, stopping, and restarting Kafka in a manner consistent with other Linux services.&lt;/p&gt;

&lt;p&gt;Zookeeper is a service that Kafka uses to manage its cluster state and configurations. It is commonly used in many distributed systems as an integral component. If you would like to know more about it, visit the official &lt;a href="https://zookeeper.apache.org/doc/current/index.html"&gt;Zookeeper docs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Create the unit file for &lt;code&gt;zookeeper&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo vi /etc/systemd/system/zookeeper.service
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Enter the following unit definition into the file:&lt;/p&gt;
&lt;div class="code-label " title="/etc/systemd/system/zookeeper.service"&gt;/etc/systemd/system/zookeeper.service&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;[Unit]
Requires=network.target remote-fs.target
After=network.target remote-fs.target

[Service]
Type=simple
User=kafka
ExecStart=/home/kafka/kafka/bin/zookeeper-server-start.sh /home/kafka/kafka/config/zookeeper.properties
ExecStop=/home/kafka/kafka/bin/zookeeper-server-stop.sh
Restart=on-abnormal

[Install]
WantedBy=multi-user.target
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;[Unit]&lt;/code&gt; section specifies that Zookeeper requires networking and the filesystem to be ready before it can start.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;[Service]&lt;/code&gt; section specifies that systemd should use the &lt;code&gt;zookeeper-server-start.sh&lt;/code&gt; and &lt;code&gt;zookeeper-server-stop.sh&lt;/code&gt; shell files for starting and stopping the service. It also specifies that Zookeeper should be restarted automatically if it exits abnormally.&lt;/p&gt;

&lt;p&gt;Next, create the systemd service file for &lt;code&gt;kafka&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo vi /etc/systemd/system/kafka.service
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Enter the following unit definition into the file:&lt;/p&gt;
&lt;div class="code-label " title="/etc/systemd/system/kafka.service"&gt;/etc/systemd/system/kafka.service&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;[Unit]
Requires=zookeeper.service
After=zookeeper.service

[Service]
Type=simple
User=kafka
ExecStart=/bin/sh -c '/home/kafka/kafka/bin/kafka-server-start.sh /home/kafka/kafka/config/server.properties &amp;gt; /home/kafka/kafka/kafka.log 2&amp;gt;&amp;amp;1'
ExecStop=/home/kafka/kafka/bin/kafka-server-stop.sh
Restart=on-abnormal

[Install]
WantedBy=multi-user.target
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;[Unit]&lt;/code&gt; section specifies that this unit file depends on &lt;code&gt;zookeeper.service&lt;/code&gt;. This will ensure that &lt;code&gt;zookeeper&lt;/code&gt; gets started automatically when the &lt;code&gt;kafa&lt;/code&gt; service starts.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;[Service]&lt;/code&gt; section specifies that systemd should use the &lt;code&gt;kafka-server-start.sh&lt;/code&gt; and &lt;code&gt;kafka-server-stop.sh&lt;/code&gt; shell files for starting and stopping the service. It also specifies that Kafka should be restarted automatically if it exits abnormally. &lt;/p&gt;

&lt;p&gt;Now that the units have been defined, start Kafka with the following command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl start kafka
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To ensure that the server has started successfully, check the journal logs for the &lt;code&gt;kafka&lt;/code&gt; unit:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;journalctl -u kafka
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see output similar to the following:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Jul 17 18:38:59 kafka-centos systemd[1]: Started kafka.service.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You now have a Kafka server listening on port &lt;code&gt;9092&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;While we have started the &lt;code&gt;kafka&lt;/code&gt; service, if we were to reboot our server, it would not be started automatically. To enable &lt;code&gt;kafka&lt;/code&gt; on server boot, run:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl enable kafka
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that we've started and enabled the services, let's check the installation.&lt;/p&gt;

&lt;h2 id="step-5-—-testing-the-installation"&gt;Step 5 — Testing the Installation&lt;/h2&gt;

&lt;p&gt;Let's publish and consume a &lt;strong&gt;"Hello World"&lt;/strong&gt; message to make sure the Kafka server is behaving correctly. Publishing messages in Kafka requires:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;em&gt;producer&lt;/em&gt;, which enables the publication of records and data to topics. &lt;/li&gt;
&lt;li&gt;A &lt;em&gt;consumer&lt;/em&gt;, which reads messages and data from topics.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;First, create a topic named &lt;code&gt;TutorialTopic&lt;/code&gt; by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;~/kafka/bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic TutorialTopic
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can create a producer from the command line using the &lt;code&gt;kafka-console-producer.sh&lt;/code&gt; script. It expects the Kafka server's hostname, port, and a topic name as arguments.&lt;/p&gt;

&lt;p&gt;Publish the string &lt;code&gt;"Hello, World"&lt;/code&gt; to the &lt;code&gt;TutorialTopic&lt;/code&gt; topic by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;echo "Hello, World" | ~/kafka/bin/kafka-console-producer.sh --broker-list localhost:9092 --topic TutorialTopic &amp;gt; /dev/null
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, you can create a Kafka consumer using the &lt;code&gt;kafka-console-consumer.sh&lt;/code&gt; script. It expects the ZooKeeper server's hostname and port, along with a topic name as arguments. &lt;/p&gt;

&lt;p&gt;The following command consumes messages from &lt;code&gt;TutorialTopic&lt;/code&gt;. Note the use of the &lt;code&gt;--from-beginning&lt;/code&gt; flag, which allows the consumption of messages that were published before the consumer was started:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;~/kafka/bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic TutorialTopic --from-beginning
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If there are no configuration issues, you should see &lt;code&gt;Hello, World&lt;/code&gt; in your terminal:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Hello, World
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The script will continue to run, waiting for more messages to be published to the topic. Feel free to open a new terminal and start a producer to publish a few more messages. You should be able to see them all in the consumer's output.&lt;/p&gt;

&lt;p&gt;When you are done testing, press &lt;code&gt;CTRL+C&lt;/code&gt; to stop the consumer script. Now that we have tested the installation, let's move on to installing KafkaT. &lt;/p&gt;

&lt;h2 id="step-6-—-installing-kafkat-optional"&gt;Step 6 — Installing KafkaT (Optional)&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/airbnb/kafkat"&gt;KafkaT&lt;/a&gt; is a tool from Airbnb that makes it easier for you to view details about your Kafka cluster and perform certain administrative tasks from the command line. Because it is a Ruby gem, you will need Ruby to use it. You will also need &lt;code&gt;ruby-devel&lt;/code&gt; and build-related packages such as &lt;code&gt;make&lt;/code&gt; and &lt;code&gt;gcc&lt;/code&gt; to be able to build the other gems it depends on. Install them using &lt;code&gt;yum&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo yum install ruby ruby-devel make gcc patch
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can now install KafkaT using the gem command: &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo gem install kafkat
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;KafkaT uses &lt;code&gt;.kafkatcfg&lt;/code&gt; as the configuration file to determine the installation and log directories of your Kafka server. It should also have an entry pointing KafkaT to your ZooKeeper instance. &lt;/p&gt;

&lt;p&gt;Create a new file called &lt;code&gt;.kafkatcfg&lt;/code&gt;: &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;vi ~/.kafkatcfg
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add the following lines to specify the required information about your Kafka server and Zookeeper instance: &lt;/p&gt;
&lt;div class="code-label " title="~/.kafkatcfg"&gt;~/.kafkatcfg&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;{
  "kafka_path": "~/kafka",
  "log_path": "/tmp/kafka-logs",
  "zk_path": "localhost:2181"
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You are now ready to use KafkaT. For a start, here's how you would use it to view details about all Kafka partitions:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kafkat partitions
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will see the following output:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Topic                 Partition   Leader      Replicas        ISRs    
TutorialTopic         0             0         [0]             [0]
__consumer_offsets    0             0         [0]                           [0]
...
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will see &lt;code&gt;TutorialTopic&lt;/code&gt;, as well as &lt;code&gt;__consumer_offsets&lt;/code&gt;, an internal topic used by Kafka for storing client-related information. You can safely ignore lines starting with &lt;code&gt;__consumer_offsets&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;To learn more about KafkaT, refer to its &lt;a href="https://github.com/airbnb/kafkat"&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id="step-7-—-setting-up-a-multi-node-cluster-optional"&gt;Step 7 — Setting Up a Multi-Node Cluster (Optional)&lt;/h2&gt;

&lt;p&gt;If you want to create a multi-broker cluster using more CentOS 7 machines, you should repeat Step 1, Step 4, and Step 5 on each of the new machines. Additionally, you should make the following changes in the &lt;code&gt;server.properties&lt;/code&gt; file for each:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The value of the &lt;code&gt;broker.id&lt;/code&gt; property should be changed such that it is unique throughout the cluster. This property uniquely identifies each server in the cluster and can have any string as its value. For example, &lt;code&gt;"server1"&lt;/code&gt;, &lt;code&gt;"server2"&lt;/code&gt;, etc. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The value of the &lt;code&gt;zookeeper.connect&lt;/code&gt; property should be changed such that all nodes point to the same ZooKeeper instance. This property specifies the Zookeeper instance's address and follows the &lt;code&gt;&amp;lt;HOSTNAME/IP_ADDRESS&amp;gt;:&amp;lt;PORT&amp;gt;&lt;/code&gt; format. For example, &lt;code&gt;"&lt;span class="highlight"&gt;203.0.113.0&lt;/span&gt;:2181"&lt;/code&gt;, &lt;code&gt;"&lt;span class="highlight"&gt;203.0.113.1&lt;/span&gt;:2181"&lt;/code&gt; etc. &lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want to have multiple ZooKeeper instances for your cluster, the value of the &lt;code&gt;zookeeper.connect&lt;/code&gt; property on each node should be an identical, comma-separated string listing the IP addresses and port numbers of all the ZooKeeper instances.&lt;/p&gt;

&lt;h2 id="step-8-—-restricting-the-kafka-user"&gt;Step 8 — Restricting the Kafka User&lt;/h2&gt;

&lt;p&gt;Now that all of the installations are done, you can remove the &lt;strong&gt;kafka&lt;/strong&gt; user's admin privileges. Before you do so, log out and log back in as any other non-root sudo user. If you are still running the same shell session you started this tutorial with, simply type &lt;code&gt;exit&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Remove the &lt;strong&gt;kafka&lt;/strong&gt; user from the sudo group:  &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo gpasswd -d kafka wheel
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To further improve your Kafka server's security, lock the &lt;strong&gt;kafka&lt;/strong&gt; user's password using the &lt;code&gt;passwd&lt;/code&gt; command. This makes sure that nobody can directly log into the server using this account: &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo passwd kafka -l
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At this point, only root or a sudo user can log in as &lt;code&gt;kafka&lt;/code&gt; by typing in the following command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo su - kafka
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the future, if you want to unlock it, use &lt;code&gt;passwd&lt;/code&gt; with the &lt;code&gt;-u&lt;/code&gt; option:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo passwd kafka -u
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You have now successfully restricted the &lt;strong&gt;kafka&lt;/strong&gt; user's admin privileges.&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;You now have Apache Kafka running securely on your CentOS server. You can make use of it in your projects by creating Kafka producers and consumers using &lt;a href="https://cwiki.apache.org/confluence/display/KAFKA/Clients"&gt;Kafka clients&lt;/a&gt;, which are available for most programming languages. To learn more about Kafka, you can also consult its &lt;a href="http://kafka.apache.org/documentation.html"&gt;documentation&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/an-introduction-to-helm-the-package-manager-for-kubernetes</id>
    <published>2018-08-02T16:08:59Z</published>
    <updated>2018-09-19T19:12:11Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/an-introduction-to-helm-the-package-manager-for-kubernetes"/>
    <title>An Introduction to Helm, the Package Manager for Kubernetes</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;Deploying applications to Kubernetes – the powerful and popular container-orchestration system – can be complex. Setting up a single application can involve creating multiple interdependent Kubernetes resources – such as pods, services, deployments, and replicasets – each requiring you to write a detailed YAML manifest file.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.helm.sh"&gt;Helm&lt;/a&gt; is a package manager for Kubernetes that allows developers and operators to more easily package, configure, and deploy applications and services onto Kubernetes clusters.&lt;/p&gt;

&lt;p&gt;Helm is now an official Kubernetes project and is part of the &lt;a href="https://www.cncf.io"&gt;Cloud Native Computing Foundation&lt;/a&gt;, a non-profit that supports open source projects in and around the Kubernetes ecosystem.&lt;/p&gt;

&lt;p&gt;In this article we will give an overview of Helm and the various abstractions it uses to simplify deploying applications to Kubernetes. If you are new to Kubernetes, it may be helpful to read &lt;a href="https://www.digitalocean.com/community/tutorials/an-introduction-to-kubernetes"&gt;&lt;em&gt;An Introduction to Kubernetes&lt;/em&gt;&lt;/a&gt; first to familiarize yourself with the basics concepts.&lt;/p&gt;

&lt;h2 id="an-overview-of-helm"&gt;An Overview of Helm&lt;/h2&gt;

&lt;p&gt;Most every programming language and operating system has its own package manager to help with the installation and maintenance of software. Helm provides the same basic feature set as many of the package managers you may already be familiar with, such as Debian's &lt;code&gt;apt&lt;/code&gt;, or Python's &lt;code&gt;pip&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Helm can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install software.&lt;/li&gt;
&lt;li&gt;Automatically install software dependencies.&lt;/li&gt;
&lt;li&gt;Upgrade software.&lt;/li&gt;
&lt;li&gt;Configure software deployments.&lt;/li&gt;
&lt;li&gt;Fetch software packages from repositories.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Helm provides this functionality through the following components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A command line tool, &lt;code&gt;helm&lt;/code&gt;, which provides the user interface to all Helm functionality.&lt;/li&gt;
&lt;li&gt;A companion server component, &lt;code&gt;tiller&lt;/code&gt;, that runs on your Kubernetes cluster, listens for commands from &lt;code&gt;helm&lt;/code&gt;, and handles the configuration and deployment of software releases on the cluster.&lt;/li&gt;
&lt;li&gt;The Helm packaging format, called &lt;em&gt;charts&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;An &lt;a href="https://github.com/helm/charts"&gt;official curated charts repository&lt;/a&gt; with prepackaged charts for popular open-source software projects.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We'll investigate the charts format in more detail next.&lt;/p&gt;

&lt;h3 id="charts"&gt;Charts&lt;/h3&gt;

&lt;p&gt;Helm packages are called &lt;em&gt;charts&lt;/em&gt;, and they consist of a few YAML configuration files and some templates that are rendered into Kubernetes manifest files. Here is the basic directory structure of a chart:&lt;/p&gt;
&lt;div class="code-label " title="Example chart directory"&gt;Example chart directory&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;package-name/
  charts/
  templates/
  Chart.yaml
  LICENSE
  README.md
  requirements.yaml
  values.yaml
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These directories and files have the following functions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;charts/:&lt;/strong&gt; Manually managed chart dependencies can be placed in this directory, though it is typically better to use &lt;code&gt;requirements.yaml&lt;/code&gt; to dynamically link dependencies.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;templates/:&lt;/strong&gt; This directory contains template files that are combined with configuration values (from &lt;code&gt;values.yaml&lt;/code&gt; and the command line) and rendered into Kubernetes manifests. The templates use the &lt;a href="https://golang.org/pkg/text/template"&gt;Go programming language's template format&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Chart.yaml:&lt;/strong&gt; A YAML file with metadata about the chart, such as chart name and version, maintainer information, a relevant website, and search keywords.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;LICENSE:&lt;/strong&gt; A plaintext license for the chart.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;README.md:&lt;/strong&gt; A readme file with information for users of the chart.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;requirements.yaml:&lt;/strong&gt; A YAML file that lists the chart's dependencies.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;values.yaml:&lt;/strong&gt; A YAML file of default configuration values for the chart.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;helm&lt;/code&gt; command can install a chart from a local directory, or from a &lt;code&gt;.tar.gz&lt;/code&gt; packaged version of this directory structure. These packaged charts can also be automatically downloaded and installed from chart repositories or &lt;em&gt;repos&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;We'll look at chart repositories next.&lt;/p&gt;

&lt;h3 id="chart-repositories"&gt;Chart Repositories&lt;/h3&gt;

&lt;p&gt;A Helm chart repo is a simple HTTP site that serves an &lt;code&gt;index.yaml&lt;/code&gt; file and &lt;code&gt;.tar.gz&lt;/code&gt; packaged charts. The &lt;code&gt;helm&lt;/code&gt; command has subcommands available to help package charts and create the required &lt;code&gt;index.yaml&lt;/code&gt; file. These files can be served by any web server, object storage service, or a static site host such as GitHub Pages.&lt;/p&gt;

&lt;p&gt;Helm comes preconfigured with a default chart repository, referred to as &lt;strong&gt;stable&lt;/strong&gt;. This repo points to a Google Storage bucket at &lt;code&gt;https://kubernetes-charts.storage.googleapis.com&lt;/code&gt;. The source for the &lt;strong&gt;stable&lt;/strong&gt; repo can be found in &lt;a href="https://github.com/helm/charts/tree/master/stable"&gt;the helm/charts Git repository on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Alternate repos can be added with the &lt;code&gt;helm repo add&lt;/code&gt; command. Some popular alternate repositories are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/helm/charts/tree/master/incubator"&gt;The official &lt;strong&gt;incubator&lt;/strong&gt; repo&lt;/a&gt; that contains charts that are not yet ready for &lt;strong&gt;stable&lt;/strong&gt;. Instructions for using &lt;strong&gt;incubator&lt;/strong&gt; can be found on &lt;a href="https://github.com/helm/charts"&gt;the official Helm charts GitHub page&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://bitnami.com/stacks/helm"&gt;Bitnami Helm Charts&lt;/a&gt; which provide some charts that aren't covered in the official &lt;strong&gt;stable&lt;/strong&gt; repo.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Whether you're installing a chart you've developed locally, or one from a repo, you'll need to configure it for your particular setup. We'll look into configs next.&lt;/p&gt;

&lt;h2 id="chart-configuration"&gt;Chart Configuration&lt;/h2&gt;

&lt;p&gt;A chart usually comes with default configuration values in its &lt;code&gt;values.yaml&lt;/code&gt; file. Some applications may be fully deployable with default values, but you'll typically need to override some of the configuration to meet your needs.&lt;/p&gt;

&lt;p&gt;The values that are exposed for configuration are determined by the author of the chart. Some are used to configure Kubernetes primitives, and some may be passed through to the underlying container to configure the application itself.&lt;/p&gt;

&lt;p&gt;Here is a snippet of some example values:&lt;/p&gt;
&lt;div class="code-label " title="values.yaml"&gt;values.yaml&lt;/div&gt;&lt;pre class="code-pre yaml"&gt;&lt;code langs=""&gt;service:
  type: ClusterIP
  port: 3306
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These are options to configure a Kubernetes &lt;em&gt;Service&lt;/em&gt; resource. You can use &lt;code&gt;helm inspect values &lt;span class="highlight"&gt;chart-name&lt;/span&gt;&lt;/code&gt; to dump all of the available configuration values for a chart.&lt;/p&gt;

&lt;p&gt;These values can be overridden by writing your own YAML file and using it when running &lt;code&gt;helm install&lt;/code&gt;, or by setting options individually on the command line with the &lt;code&gt;--set&lt;/code&gt; flag. You only need to specify those values that you want to change from the defaults.&lt;/p&gt;

&lt;p&gt;A Helm chart deployed with a particular configuration is called a &lt;em&gt;release&lt;/em&gt;. We will talk about releases next.&lt;/p&gt;

&lt;h2 id="releases"&gt;Releases&lt;/h2&gt;

&lt;p&gt;During the installation of a chart, Helm combines the chart's templates with the configuration specified by the user and the defaults in &lt;code&gt;value.yaml&lt;/code&gt;. These are rendered into Kubernetes manifests that are then deployed via the Kubernetes API. This creates a &lt;em&gt;release&lt;/em&gt;, a specific configuration and deployment of a particular chart.&lt;/p&gt;

&lt;p&gt;This concept of releases is important, because you may want to deploy the same application more than once on a cluster. For instance, you may need multiple MySQL servers with different configurations.&lt;/p&gt;

&lt;p&gt;You also will probably want to upgrade different instances of a chart individually. Perhaps one application is ready for an updated MySQL server but another is not. With Helm, you upgrade each release individually.&lt;/p&gt;

&lt;p&gt;You might upgrade a release because its chart has been updated, or because you want to update the release's configuration. Either way, each upgrade will create a new &lt;em&gt;revision&lt;/em&gt; of a release, and Helm will allow you to easily roll back to previous revisions in case there's an issue.&lt;/p&gt;

&lt;h2 id="creating-charts"&gt;Creating Charts&lt;/h2&gt;

&lt;p&gt;If you can't find an existing chart for the software you are deploying, you may want to create your own. Helm can output the scaffold of a chart directory with &lt;code&gt;helm create &lt;span class="highlight"&gt;chart-name&lt;/span&gt;&lt;/code&gt;. This will create a folder with the files and directories we discussed in the &lt;a href="#charts"&gt;Charts&lt;/a&gt; section above.&lt;/p&gt;

&lt;p&gt;From there, you'll want to fill out your chart's metadata in &lt;code&gt;Chart.yaml&lt;/code&gt; and put your Kubernetes manifest files into the &lt;code&gt;templates&lt;/code&gt; directory. You'll then need to extract relevant configuration variables out of your manifests and into &lt;code&gt;values.yaml&lt;/code&gt;, then include them back into your manifest templates using &lt;a href="https://golang.org/pkg/text/template"&gt;the templating system&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;helm&lt;/code&gt; command has many subcommands available to help you test, package, and serve your charts. For more information, please read &lt;a href="https://docs.helm.sh/developing_charts"&gt;the official Helm documentation on developing charts&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;In this article we reviewed Helm, the package manager for Kubernetes. We overviewed the Helm architecture and the individual &lt;code&gt;helm&lt;/code&gt; and &lt;code&gt;tiller&lt;/code&gt; components, detailed the Helm charts format, and looked at chart repositories. We also looked into how to configure a Helm chart and how configurations and charts are combined and deployed as releases on Kubernetes clusters. Finally, we touched on the basics of creating a chart when a suitable chart isn't already available.&lt;/p&gt;

&lt;p&gt;For more information about Helm, take a look at &lt;a href="https://docs.helm.sh"&gt;the official Helm documentation&lt;/a&gt;. To find official charts for Helm, check out &lt;a href="https://github.com/helm/charts"&gt;the official helm/charts Git repository on GitHub&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-install-the-django-web-framework-on-ubuntu-18-04</id>
    <published>2018-08-02T19:53:11Z</published>
    <updated>2018-08-13T15:49:57Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-install-the-django-web-framework-on-ubuntu-18-04"/>
    <title>How To Install the Django Web Framework on Ubuntu 18.04</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.djangoproject.com/"&gt;Django&lt;/a&gt; is a full-featured Python web framework for developing dynamic websites and applications.  Using Django, you can quickly create Python web applications and rely on the framework to do a good deal of the heavy lifting.&lt;/p&gt;

&lt;p&gt;In this guide, you will get Django up and running on an Ubuntu 18.04 server.  After installation, you will start a new project to use as the basis for your site.&lt;/p&gt;

&lt;h2 id="different-methods"&gt;Different Methods&lt;/h2&gt;

&lt;p&gt;There are different ways to install Django, depending upon your needs and how you want to configure your development environment.  These have different advantages and one method may lend itself better to your specific situation than others.&lt;/p&gt;

&lt;p&gt;Some of the different methods include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Global install from packages&lt;/strong&gt;:  The official Ubuntu repositories contain Django packages that can be installed with the conventional &lt;code&gt;apt&lt;/code&gt; package manager.  This is simple, but not as flexible as some other methods. Also, the version contained in the repositories may lag behind the official versions available from the project.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Install with &lt;code&gt;pip&lt;/code&gt; in a virtual environment&lt;/strong&gt;: You can create a self-contained environment for your projects using tools like &lt;code&gt;venv&lt;/code&gt; and &lt;code&gt;virtualenv&lt;/code&gt;. A virtual environment allows you to install Django in a project directory without affecting the larger system, along with other per-project customizations and packages. This is typically the most practical and recommended approach to working with Django.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Development version install with &lt;code&gt;git&lt;/code&gt;&lt;/strong&gt;: If you wish to install the latest development version instead of the stable release, you can acquire the code from the Git repo.  This is necessary to get the latest features/fixes and can be done within your virtual environment.  Development versions do not have the same stability guarantees as more stable versions, however.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;Before you begin, you should have a non-root user with sudo privileges available on your Ubuntu 18.04 server. To set this up, follow our &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-18-04"&gt;Ubuntu 18.04 initial server setup guide&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id="global-install-from-packages"&gt;Global Install from Packages&lt;/h2&gt;

&lt;p&gt;If you wish to install Django using the Ubuntu repositories, the process is very straightforward.&lt;/p&gt;

&lt;p&gt;First, update your local package index with &lt;code&gt;apt&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt update
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, check which version of Python you have installed. 18.04 ships with Python 3.6 by default, which you can verify by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;python3 -V
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see output like this:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Python &lt;span class="highlight"&gt;3.6.5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, install Django:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install python3-django
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can test that the installation was successful by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;django-admin --version
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;&lt;span class="highlight"&gt;1.11.11&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This means that the software was successfully installed.  You may also notice that the Django version is not the latest stable version. To learn more about how to use the software, skip ahead to learn &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-the-django-web-framework-on-ubuntu-18-04#creating-a-sample-project"&gt;how to create sample project&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id="install-with-pip-in-a-virtual-environment"&gt;Install with pip in a Virtual Environment&lt;/h2&gt;

&lt;p&gt;The most flexible way to install Django on your system is within a virtual environment. We will show you how to install Django in a virtual environment that we will create with the &lt;code&gt;venv&lt;/code&gt; module, part of the standard Python 3 library.  This tool allows you to create virtual Python environments and install Python packages without affecting the rest of the system. You can therefore select Python packages on a per-project basis, regardless of conflicts with other projects' requirements.&lt;/p&gt;

&lt;p&gt;Let's begin by refreshing the local package index:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt update
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Check the version of Python you have installed:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;python3 -V
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Python &lt;span class="highlight"&gt;3.6.5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, let's install &lt;code&gt;pip&lt;/code&gt; from the Ubuntu repositories:  &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install python3-pip
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once &lt;code&gt;pip&lt;/code&gt; is installed, you can use it to install the &lt;code&gt;venv&lt;/code&gt; package:  &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install python3-venv
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, whenever you start a new project, you can create a virtual environment for it. Start by creating and moving into a new project directory:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;mkdir ~/&lt;span class="highlight"&gt;newproject&lt;/span&gt;
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;cd ~/&lt;span class="highlight"&gt;newproject&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, create a virtual environment within the project directory using the &lt;code&gt;python&lt;/code&gt; command that's compatible with your version of Python. We will call our virtual environment &lt;code&gt;&lt;span class="highlight"&gt;my_env&lt;/span&gt;&lt;/code&gt;, but you should name it something descriptive:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;python3&lt;span class="highlight"&gt;.6&lt;/span&gt; -m venv &lt;span class="highlight"&gt;my_env&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will install standalone versions of Python and &lt;code&gt;pip&lt;/code&gt; into an isolated directory structure within your project directory. A directory will be created with the name you select, which will hold the file hierarchy where your packages will be installed.&lt;/p&gt;

&lt;p&gt;To install packages into the isolated environment, you must activate it by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;source &lt;span class="highlight"&gt;my_env&lt;/span&gt;/bin/activate
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Your prompt should change to reflect that you are now in your virtual environment.  It will look something like &lt;code&gt;(&lt;span class="highlight"&gt;my_env&lt;/span&gt;)username@hostname:~/newproject$&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In your new environment, you can use &lt;code&gt;pip&lt;/code&gt; to install Django.  Regardless of your Python version, &lt;code&gt;pip&lt;/code&gt; should just be called &lt;code&gt;pip&lt;/code&gt; when you are in your virtual environment.  Also note that you &lt;em&gt;do not&lt;/em&gt; need to use &lt;code&gt;sudo&lt;/code&gt; since you are installing locally:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="(my_env) $"&gt;pip install django
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can verify the installation by typing:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="(my_env) $"&gt;django-admin --version
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;&lt;span class="highlight"&gt;2.1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that your version may differ from the version shown here. &lt;/p&gt;

&lt;p&gt;To leave your virtual environment, you need to issue the &lt;code&gt;deactivate&lt;/code&gt; command from anywhere on the system:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="(my_env) $"&gt;deactivate
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Your prompt should revert to the conventional display.  When you wish to work on your project again, re-activate your virtual environment by moving back into your project directory and activating:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cd ~/&lt;span class="highlight"&gt;newproject&lt;/span&gt;
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;source &lt;span class="highlight"&gt;my_env&lt;/span&gt;/bin/activate
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="development-version-install-with-git"&gt;Development Version Install with Git&lt;/h2&gt;

&lt;p&gt;If you need a development version of Django, you can download and install Django from its Git repository. Let's do this from within a virtual environment.&lt;/p&gt;

&lt;p&gt;First, let's update the local package index:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt update
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Check the version of Python you have installed:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;python3 -V
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Python &lt;span class="highlight"&gt;3.6.5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, install &lt;code&gt;pip&lt;/code&gt; from the official repositories:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install python3-pip
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Install the &lt;code&gt;venv&lt;/code&gt; package to create your virtual environment:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install python3-venv
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The next step is cloning the Django repository. Between releases, this repository will have more up-to-date features and bug fixes at the possible expense of stability.  You can clone the repository to a directory called &lt;code&gt;~/&lt;span class="highlight"&gt;django-dev&lt;/span&gt;&lt;/code&gt; within your home directory by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;git clone git://github.com/django/django ~/&lt;span class="highlight"&gt;django-dev&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Change to this directory:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cd ~/&lt;span class="highlight"&gt;django-dev&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Create a virtual environment using the &lt;code&gt;python&lt;/code&gt; command that's compatible with your installed version of Python:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;python3&lt;span class="highlight"&gt;.6&lt;/span&gt; -m venv &lt;span class="highlight"&gt;my_env&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Activate it:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;source &lt;span class="highlight"&gt;my_env&lt;/span&gt;/bin/activate
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, you can install the repository using &lt;code&gt;pip&lt;/code&gt;. The &lt;code&gt;-e&lt;/code&gt; option will install in "editable" mode, which is necessary when installing from version control:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="(my_env) $"&gt;pip install -e ~/&lt;span class="highlight"&gt;django-dev&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can verify that the installation was successful by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;django-admin --version
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;&lt;span class="highlight"&gt;2.2.dev20180802155335&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Again, the version you see displayed may not match what is shown here.&lt;/p&gt;

&lt;p&gt;You now have the latest version of Django in your virtual environment.&lt;/p&gt;

&lt;h2 id="creating-a-sample-project"&gt;Creating a Sample Project&lt;/h2&gt;

&lt;p&gt;With Django installed, you can begin building your project. We will go over how to create a project and test it on your development server using a virtual environment. &lt;/p&gt;

&lt;p&gt;First, create a directory for your project and change into it:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;mkdir ~/&lt;span class="highlight"&gt;django-test&lt;/span&gt;
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;cd ~/&lt;span class="highlight"&gt;django-test&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, create your virtual environment: &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;python3&lt;span class="highlight"&gt;.6&lt;/span&gt; -m venv &lt;span class="highlight"&gt;my_env&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Activate the environment:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;source &lt;span class="highlight"&gt;my_env&lt;/span&gt;/bin/activate
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Install Django: &lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="(my_env) $"&gt;pip install django
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To build your project, you can use &lt;code&gt;django-admin&lt;/code&gt; with the &lt;code&gt;startproject&lt;/code&gt; command. We will call our project &lt;code&gt;&lt;span class="highlight"&gt;djangoproject&lt;/span&gt;&lt;/code&gt;, but you can replace this with a different name. &lt;code&gt;startproject&lt;/code&gt; will create a directory within your current working directory that includes: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A management script, &lt;code&gt;manage.py&lt;/code&gt;, which you can use to administer various Django-specific tasks.&lt;/li&gt;
&lt;li&gt;A directory (with the same name as the project) that includes the actual project code. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To avoid having too many nested directories, however, let's tell Django to place the management script and inner directory in the &lt;em&gt;current&lt;/em&gt; directory (notice the ending dot):&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="(my_env) $"&gt;django-admin startproject &lt;span class="highlight"&gt;djangoproject&lt;/span&gt; .
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To migrate the database (this example uses SQLite by default), let's use the &lt;code&gt;migrate&lt;/code&gt; command with &lt;code&gt;manage.py&lt;/code&gt;. &lt;a href="https://docs.djangoproject.com/en/2.0/topics/migrations/"&gt;Migrations&lt;/a&gt; apply any changes you've made to your Django &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-create-django-models"&gt;models&lt;/a&gt; to your database schema. &lt;/p&gt;

&lt;p&gt;To migrate the database, type:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="(my_env) $"&gt;python manage.py migrate
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will see output like the following:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying sessions.0001_initial... OK
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, let's create an administrative user so that you can use the &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-enable-and-connect-the-django-admin-interface"&gt;Djano admin interface&lt;/a&gt;. Let's do this with the &lt;code&gt;createsuperuser&lt;/code&gt; command: &lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="(my_env) $"&gt;python manage.py createsuperuser
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will be prompted for a username, an email address, and a password for your user.&lt;/p&gt;

&lt;h2 id="modifying-allowed_hosts-in-the-django-settings"&gt;Modifying ALLOWED_HOSTS in the Django Settings&lt;/h2&gt;

&lt;p&gt;To successfully test your application, you will need to modify one of the directives in the Django settings.&lt;/p&gt;

&lt;p&gt;Open the settings file by typing:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="(my_env) $"&gt;nano ~/&lt;span class="highlight"&gt;django-test&lt;/span&gt;/&lt;span class="highlight"&gt;djangoproject&lt;/span&gt;/settings.py
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Inside, locate the &lt;code&gt;ALLOWED_HOSTS&lt;/code&gt; directive. This defines a whitelist of addresses or domain names that may be used to connect to the Django instance. An incoming request with a &lt;strong&gt;Host&lt;/strong&gt; header that is not in this list will raise an exception.  Django requires that you set this to prevent a certain class of security vulnerability.&lt;/p&gt;

&lt;p&gt;In the square brackets, list the IP addresses or domain names that are associated with your Django server.  Each item should be listed in quotations, with separate entries separated by a comma.  If you want requests for an entire domain and any subdomains, prepend a period to the beginning of the entry:&lt;/p&gt;
&lt;div class="code-label " title="~/django-test/djangoproject/settings.py"&gt;~/django-test/djangoproject/settings.py&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .
ALLOWED_HOSTS = ['&lt;span class="highlight"&gt;your_server_ip_or_domain&lt;/span&gt;', '&lt;span class="highlight"&gt;your_second_ip_or_domain&lt;/span&gt;', &lt;span class="highlight"&gt;. . .&lt;/span&gt;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When you are finished, save the file and exit your editor. &lt;/p&gt;

&lt;h2 id="testing-the-development-server"&gt;Testing the Development Server&lt;/h2&gt;

&lt;p&gt;Once you have a user, you can start up the Django development server to see what a fresh Django project looks like.  You should only use this for development purposes. When you are ready to deploy, be sure to follow &lt;a href="https://docs.djangoproject.com/en/2.1/howto/deployment/checklist/"&gt;Django's guidelines on deployment&lt;/a&gt; carefully.&lt;/p&gt;

&lt;p&gt;Before you try the development server, make sure you open the appropriate port in your firewall. If you followed the initial server setup guide and are using UFW, you can open port &lt;code&gt;8000&lt;/code&gt; by typing: &lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="(my_env) $"&gt;sudo ufw allow 8000
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Start the development server:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="(my_env) $"&gt;python manage.py runserver &lt;span class="highlight"&gt;your_server_ip&lt;/span&gt;:8000
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Visit your server's IP address followed by &lt;code&gt;:8000&lt;/code&gt; in your web browser:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;http://&lt;span class="highlight"&gt;your_server_ip&lt;/span&gt;:8000
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see something that looks like this:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/django_install_18_04/django_landing_page_18_04.png" alt="Django public page"&gt;&lt;/p&gt;

&lt;p&gt;To access the admin interface, add &lt;code&gt;/admin/&lt;/code&gt; to the end of your URL: &lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;http://&lt;span class="highlight"&gt;your_server_ip&lt;/span&gt;:8000/admin/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will take you to a log in screen:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/eng_python/django/django-admin-login.png" alt="Django admin login"&gt;&lt;/p&gt;

&lt;p&gt;If you enter the admin username and password that you just created, you will have access to the main admin section of the site:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/eng_python/django/django-admin-panel.png" alt="Django admin page"&gt;&lt;/p&gt;

&lt;p&gt;For more information about working with the Django admin interface, please see &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-enable-and-connect-the-django-admin-interface"&gt;"How To Enable and Connect the Django Admin Interface."&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When you are finished looking through the default site, you can stop the development server by typing &lt;code&gt;CTRL-C&lt;/code&gt; in your terminal.&lt;/p&gt;

&lt;p&gt;The Django project you've created provides the structural basis for designing a more complete site.  Check out the Django documentation for more information about how to build your applications and customize your site.&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;You should now have Django installed on your Ubuntu 18.04 server, providing the main tools you need to create powerful web applications.  You should also know how to start a new project and launch the developer server.  Leveraging a complete web framework like Django can help make development faster, allowing you to concentrate only on the unique aspects of your applications.&lt;/p&gt;

&lt;p&gt;If you would like more information about working with Django, including in-depth discussions of things like &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-create-django-models"&gt;models&lt;/a&gt; and &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-create-django-views"&gt;views&lt;/a&gt;, please see our &lt;a href="https://www.digitalocean.com/community/tutorial_series/django-development"&gt;Django development series&lt;/a&gt;. &lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/apache-ubuntu-18-04-ru</id>
    <published>2018-08-03T21:25:34Z</published>
    <updated>2018-08-03T21:25:41Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/apache-ubuntu-18-04-ru"/>
    <title>Как установить веб-сервер Apache в Ubuntu 18.04</title>
    <content type="html">&lt;h3 id="Введение"&gt;Введение&lt;/h3&gt;

&lt;p&gt;HTTP сервер Apache является самым широко используемым веб-сервером в мире. Он предоставляет множество удобных функций включая динамически загружаемые модули, широкую поддержку мультимедиа, и интеграцию с другим популярным программным обеспечением.&lt;/p&gt;

&lt;p&gt;В этом руководстве мы расскажем, как установить веб-сервер Apache на ваш сервер с Ubuntu 18.04.&lt;/p&gt;

&lt;h2 id="Необходимые-условия"&gt;Необходимые условия&lt;/h2&gt;

&lt;p&gt;Перед тем, как начать следовать шагам, описанным в этом руководстве, вам необходимо настроить отдельный, не-рутовый (non-root) профиль пользователя на вашем сервере с Ubuntu 18.04. Кроме того, вам потребуется настроить базовый файрвол для блокирования всех портов, кроме необходимых для работы Apache. Вы можете ознакомиться с процессом настройки аккаунта пользователя и настройкой файрвола на вашем сервере следуя шагам нашего &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-18-04"&gt;руководства по первичной настройке сервера на Ubuntu 18.04&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;После завершения создания аккаунта войдите на ваш сервер с помощью вновь созданного пользователя.&lt;/p&gt;

&lt;h2 id="Шаг-1-Установка-apache"&gt;Шаг 1 - Установка Apache&lt;/h2&gt;

&lt;p&gt;Apache доступен из дефолтных репозиториев Ubuntu, что позволяет устанавливать его с помощью средств управления пакетами. &lt;/p&gt;

&lt;p&gt;Давайте начнём с обновления локального индекса пакетов:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt update
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Далее установим пакет &lt;code&gt;apache2&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install apache2
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;После подтверждения установки &lt;code&gt;apt&lt;/code&gt; установит Apache и все необходимые зависимости.&lt;/p&gt;

&lt;h2 id="Шаг-2-Настройка-файрвола"&gt;Шаг 2 - Настройка файрвола&lt;/h2&gt;

&lt;p&gt;Перед тестированием установки Apache необходимо изменить настройки файрвола для разрешения доступа извне к дефолным веб-портам. Если вы следовали инструкциям по настройке файрвола из руководства по первичной настройке сервера, ваш файрвол UFW уже должен быть настроен таким образом, чтобы ограничивать доступ к вашему серверу.&lt;/p&gt;

&lt;p&gt;В процессе установки Apache регистрирует себя в конфигурации UFW, создавая несколько профилей приложения, которые могут быть использованы для включения и отключения доступа к Apache через файрвол.&lt;/p&gt;

&lt;p&gt;Выведем профили приложений &lt;code&gt;ufw&lt;/code&gt; следующей командой:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw app list
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Вы увидите список приложений пользователей:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Вывод"&gt;Вывод&lt;/div&gt;Available applications:
  Apache
  Apache Full
  Apache Secure
  OpenSSH
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Как видно из этого вывода, для Apache доступно три профиля:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Apache&lt;/strong&gt;: этот профиль открывает порт 80 (обычный, не шифрованный веб-трафик).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Apache Full&lt;/strong&gt;: этот профиль открывает порты 80 (обычный, не шифрованный веб-трафик) и 443 (трафик шифруется с помощью TLS/SSL).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Apache Secure&lt;/strong&gt;: этот профиль открывает только порт 443 (трафик шифруется с помощью TLS/SSL).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Рекомендуется включать самый ограниченный профиль, который будет позволять входящий трафик. Поскольку мы не настраивали SSL для нашего сервера в этом руководстве, нам потребуется включить только порт 80:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw allow 'Apache'
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Вы можете проверить внесённые изменения командой:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw status
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;В выводе вы должны видеть, что HTTP трафик разрешён:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Вывод"&gt;Вывод&lt;/div&gt;Status: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere                  
Apache                     ALLOW       Anywhere                  
OpenSSH (v6)               ALLOW       Anywhere (v6)             
Apache (v6)                ALLOW       Anywhere (v6)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Как видно из этого вывода профиль был включен для разрешения доступа к веб-серверу.&lt;/p&gt;

&lt;h2 id="Шаг-3-Проверка-вашего-веб-сервера"&gt;Шаг 3 - Проверка вашего веб-сервера&lt;/h2&gt;

&lt;p&gt;После завершения процесса установки Ubuntu 18.04 запустит Apache. Веб-сервер уже должен быть запущен.&lt;/p&gt;

&lt;p&gt;Проверим в системе инициализации &lt;code&gt;systemd&lt;/code&gt;, что сервис работает, следующей командой:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl status apache2
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Вывод"&gt;Вывод&lt;/div&gt;● apache2.service - The Apache HTTP Server
   Loaded: loaded (/lib/systemd/system/apache2.service; enabled; vendor preset: enabled)
  Drop-In: /lib/systemd/system/apache2.service.d
           └─apache2-systemd.conf
   Active: &lt;span class="highlight"&gt;active (running)&lt;/span&gt; since Tue 2018-04-24 20:14:39 UTC; 9min ago
 Main PID: 2583 (apache2)
    Tasks: 55 (limit: 1153)
   CGroup: /system.slice/apache2.service
           ├─2583 /usr/sbin/apache2 -k start
           ├─2585 /usr/sbin/apache2 -k start
           └─2586 /usr/sbin/apache2 -k start
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Как видно из представленного вывода, сервис выглядит работающим корректно. Тем не менее, самый надёжный способ проверить работу Apache - это запросить веб-страницу.&lt;/p&gt;

&lt;p&gt;Вы можете запросить дефолтную веб-страницу Apache с помощью IP адреса вашего сервера. Если вы не знаете IP адрес вашего сервера, вы можете найти его несколькими способами с помощью командной строки.&lt;/p&gt;

&lt;p&gt;Введите следующую команду:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;hostname -I
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Она вернёт несколько адресов, разделённых пробелами. Вы можете попробовать каждый из них в вашем веб-браузере.&lt;/p&gt;

&lt;p&gt;Другой способ заключается в использовании команды, которая позволяет увидеть ваш IP адрес из другого места в сети Интернет:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;curl -4 icanhazip.com
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;После того, как вы найдёте IP адрес вашего сервера, введите его в свой веб-браузер:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;http://&lt;span class="highlight"&gt;IP_адрес_вашего_сервера&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Вы должны увидеть дефолтную страницу Apache для Ubuntu 18.04:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/how-to-install-lamp-ubuntu-16/small_apache_default.png" alt="Дефолтная страница Apache"&gt;&lt;/p&gt;

&lt;p&gt;Эта страница свидетельствует о том, что Apache работает корректно. На этой странице также представлена базовая информация о важных файлах и директориях Apache.&lt;/p&gt;

&lt;h2 id="Шаг-4-Управление-процессом-apache"&gt;Шаг 4 - Управление процессом Apache&lt;/h2&gt;

&lt;p&gt;Теперь, когда у вас есть работающий веб-сервер, рассмотрим некоторые базовые команды для управления им.&lt;/p&gt;

&lt;p&gt;Для остановки себ-сервера наберите:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl stop apache2
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Для запуска остановленного сервера наберите:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl start apache2
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Для перезапуска сервиса наберите:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl restart apache2
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Если вы вносите какие-то изменения в конфигурацию, Apache зачастую может перезагружаться без потери открытых соединений. Для этого наберите команду:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl reload apache2
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;По умолчанию Apache сконфигурирован на запуск при загрузке сервера. Вы можете отключить такое поведение следующей командой:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl disable apache2
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Для повторного включения сервиса при загрузке сервера наберите:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl enable apache2
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Теперь Apache должен опять запускаться автоматически при загрузке сервера.&lt;/p&gt;

&lt;h2 id="Шаг-5-Настройка-виртуальных-хостов-рекомендуется"&gt;Шаг 5 - Настройка виртуальных хостов (рекомендуется)&lt;/h2&gt;

&lt;p&gt;При использовании веб-сервера Apache вы можете использовать &lt;em&gt;виртуальные хосты&lt;/em&gt; (аналог серверных блоков в Nginx) для хранения конфигурационных настроек разных сайтов. Это позволяет иметь более одного сайта на одном сервере. В этом руководстве мы будем для примера использовать доменное имя &lt;strong&gt;example.com&lt;/strong&gt;, но вам &lt;strong&gt;следует заменить его вашим собственным доменным именем&lt;/strong&gt;. Для того, чтобы узнать больше о настройке доменных имён в DigitalOcean, рекомендуем ознакомиться с нашим &lt;a href="https://www.digitalocean.com/community/tutorials/an-introduction-to-digitalocean-dns"&gt;Введением в DNS DigitalOcean&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Apache для Ubuntu 18.04 уже имеет один виртуальный хост, включенный по умолчанию, который настроен на отдачу документов из директории &lt;code&gt;/var/www/html&lt;/code&gt;. Хотя это и удобно для обслуживания одного сайта, это становится неудобным, когда сайтов несколько. Вместо того, чтобы изменять &lt;code&gt;/var/www/html&lt;/code&gt;, давайте создадим новую структуру директорий внутри &lt;code&gt;/var/www&lt;/code&gt; для нашего сайта &lt;strong&gt;example.com&lt;/strong&gt;, оставив &lt;code&gt;/var/www/html&lt;/code&gt; для показа дефолтной страницы пользователям в случаях, когда клиентский запрос не совпадает ни с одним из настроенных доменных имён.&lt;/p&gt;

&lt;p&gt;Создайте директорию для &lt;strong&gt;example.com&lt;/strong&gt; используя флаг &lt;code&gt;-p&lt;/code&gt; для создания необходимых родительских директорий:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo mkdir -p /var/www/&lt;span class="highlight"&gt;example.com&lt;/span&gt;/html
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Далее настройте владельца директории с помощью переменной окружения &lt;code&gt;$USER&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo chown -R $USER:$USER /var/www/&lt;span class="highlight"&gt;example.com&lt;/span&gt;/html
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Теперь права должны для корневой директории быть настроены правильным образом при условии, что вы не меняли своё значение &lt;code&gt;umask&lt;/code&gt;. На всякий случай мы можем удостовериться в этом командой:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo chmod -R 755 /var/www/&lt;span class="highlight"&gt;example.com&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Далее создадим страницу &lt;code&gt;index.html&lt;/code&gt; в &lt;code&gt;nano&lt;/code&gt; или любом другом текстовом редакторе:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nano /var/www/&lt;span class="highlight"&gt;example.com&lt;/span&gt;/html/index.html
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Добавим в файл следующий HTML:&lt;/p&gt;
&lt;div class="code-label " title="/var/www/example.com/html/index.html"&gt;/var/www/example.com/html/index.html&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&amp;lt;html&amp;gt;
    &amp;lt;head&amp;gt;
        &amp;lt;title&amp;gt;Welcome to &lt;span class="highlight"&gt;Example.com&lt;/span&gt;!&amp;lt;/title&amp;gt;
    &amp;lt;/head&amp;gt;
    &amp;lt;body&amp;gt;
        &amp;lt;h1&amp;gt;Success!  The &lt;span class="highlight"&gt;example.com&lt;/span&gt; server block is working!&amp;lt;/h1&amp;gt;
    &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Сохраните и закройте файл.&lt;/p&gt;

&lt;p&gt;Для того, чтобы Apache мог отдавать этот контент, нам необходимо настроить виртуальный хост с корректными настройками. Вместо того, чтобы редактировать существующий файл виртуального хоста &lt;code&gt;/etc/apache2/sites-available/000-default.conf&lt;/code&gt;, создадим новый файл для нашего сайта - &lt;code&gt;/etc/apache2/sites-available/&lt;span class="highlight"&gt;example.com&lt;/span&gt;.conf&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/apache2/sites-available/&lt;span class="highlight"&gt;example.com&lt;/span&gt;.conf
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Скопируйте следующий текст настроек виртуального хоста в созданный файл:&lt;/p&gt;
&lt;div class="code-label " title="/etc/apache2/sites-available/example.com.conf"&gt;/etc/apache2/sites-available/example.com.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&amp;lt;VirtualHost *:80&amp;gt;
    ServerAdmin &lt;span class="highlight"&gt;admin@example.com&lt;/span&gt;
    ServerName &lt;span class="highlight"&gt;example.com&lt;/span&gt;
    ServerAlias &lt;span class="highlight"&gt;www.example.com&lt;/span&gt;
    DocumentRoot /var/www/&lt;span class="highlight"&gt;example.com&lt;/span&gt;/html
    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined
&amp;lt;/VirtualHost&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Обратите внимание, что мы обновили &lt;code&gt;DocumentRoot&lt;/code&gt; на адрес нашей новой директории, и &lt;code&gt;ServerAdmin&lt;/code&gt; на адрес электронной почты, доступный для администратора &lt;strong&gt;example.com&lt;/strong&gt;. Мы также добавили две директивы: &lt;code&gt;ServerName&lt;/code&gt;, которая устанавливает базовое доменное имя, которое должно использоваться для хоста, а также &lt;code&gt;ServerAlias&lt;/code&gt;, которая определяет другие имена, которые должны использоваться для отображения хоста так же, как и базовое доменное имя.&lt;/p&gt;

&lt;p&gt;Сохраните и закройте файл после внесения изменений.&lt;/p&gt;

&lt;p&gt;Теперь активируем профиль сайта с помощью утилиты &lt;code&gt;a2ensite&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo a2ensite &lt;span class="highlight"&gt;example.com&lt;/span&gt;.conf
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Деактивируем дефолтный сайт, определённый в &lt;code&gt;000-default.conf&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo a2dissite 000-default.conf
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Далее проверим наши настройки на наличие ошибок:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apache2ctl configtest
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Вы должны увидеть следующий вывод:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Вывод"&gt;Вывод&lt;/div&gt;Syntax OK
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Перезапустите Apache для применения внесённых изменений:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl restart apache2
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Теперь Apache должен работать с вашим доменным именем. Вы можете проверить это введя &lt;code&gt;http://&lt;span class="highlight"&gt;example.com&lt;/span&gt;&lt;/code&gt; в вашем браузере, где в результате вы должны увидеть что-то в этом роде:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/apache_virt_hosts_1404/example.png" alt="Успешная работа Apache"&gt;&lt;/p&gt;

&lt;h2 id="Шаг-6-Важные-файлы-и-директории-apache"&gt;Шаг 6 - Важные файлы и директории Apache&lt;/h2&gt;

&lt;p&gt;Теперь, когда вы знаете, как управлять сервисом Apache, вам стоит ознакомиться с важными файлами и директориями Apache.&lt;/p&gt;

&lt;h3 id="Контент"&gt;Контент&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/var/www/html&lt;/code&gt;: фактический веб-контент, который по умолчанию состоит только из дефолтной страницы Apache, которую мы видели ранее, хранится в директории &lt;code&gt;/var/www/html&lt;/code&gt;. Это может быть изменено в конфигурационных файлах Apache.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id="Конфигурация-сервера"&gt;Конфигурация сервера&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/etc/apache2&lt;/code&gt;: это конфигурационная директория Apache. Все файлы конфигурации Apache находятся здесь.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/etc/apache2/apache2.conf&lt;/code&gt;: главный конфигурационный файл Apache. Изменения в этом файле влияют на глобальную конфигурацию Apache. Этот файл отвечает за загрузку многих других файлов из конфигурационной директории.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/etc/apache2/ports.conf&lt;/code&gt;: этот файл определяет порты, которые Apache будет слушать. По умолчанию Apache слушает порт 80, а также порт 443 при условии, что модуль для работы с SSL включен.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/etc/apache2/sites-available/&lt;/code&gt;: в этой директории хранятся файлы виртуальных хостов. Apache не использует файлы из этой директории, если ссылки на них нет в директории &lt;code&gt;sites-enabled&lt;/code&gt;. Обычно настройка всех файлов виртуальных хостов осуществляется в этой директории, а активация хоста происходит путём создания ссылки в другой директории командой &lt;code&gt;a2ensite&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/etc/apache2/sites-enabled/&lt;/code&gt;: директория, в которой хранятся активированные виртуальные хосты. Обычно это делается путём создания ссылки на файл конфигурации хоста из директории &lt;code&gt;sites-available&lt;/code&gt; с помощью команды &lt;code&gt;a2ensite&lt;/code&gt;. Apache читает конфигурационный файлы и ссылки из этой директории при запуске или перезапуске.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/etc/apache2/conf-available/&lt;/code&gt;, &lt;code&gt;/etc/apache2/conf-enabled/&lt;/code&gt;: эти директории связаны друг с другом так же, как и &lt;code&gt;sites-available&lt;/code&gt; и &lt;code&gt;sites-enabled&lt;/code&gt; связаны друг с другом, но используются для хранения фрагментов конфигурации, которые не принадлежат виртуальным хостам. Файлы в директории &lt;code&gt;conf-available&lt;/code&gt; могут быть включены командой &lt;code&gt;a2enconf&lt;/code&gt; и выключены командой &lt;code&gt;a2disconf&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/etc/apache2/mods-available/&lt;/code&gt;, &lt;code&gt;/etc/apache2/mods-enabled/&lt;/code&gt;: эти директории содержат, соответственно, доступные и активные модули. Файлы, оканчивающиеся на &lt;code&gt;.load&lt;/code&gt;, содержат фрагменты для загрузки конкретных модулей, а файлы, оканчивающиеся на &lt;code&gt;.conf&lt;/code&gt;, содержат настройки этих модулей. Модули можно активировать командой &lt;code&gt;a2enmod&lt;/code&gt; и деактивировать командой &lt;code&gt;a2dismod&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id="Серверные-логи"&gt;Серверные логи&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/var/log/apache2/access.log&lt;/code&gt;: по умолчанию каждый запрос к вашему веб-серверу записывается в этом файле, если только Apache не настроен на другое поведение.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/var/log/apache2/error.log&lt;/code&gt;: по умолчанию все ошибки записываются в этот файл. Директива &lt;code&gt;LogLevel&lt;/code&gt; в конфигурации Apache определяет, насколько детальными должны быть записи об ошибках.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="Заключение"&gt;Заключение&lt;/h2&gt;

&lt;p&gt;Теперь, когда ваш веб-сервер установлен, у вас есть множество вариантов того, что делать дальше. Если вы хотите построить более полный стек приложений, вы можете ознакомиться с нашим &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-linux-apache-mysql-php-lamp-stack-ubuntu-18-04"&gt;руководством по установке и настройке стека LAMP на Ubuntu 18.04&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/apache-let-s-encrypt-ubuntu-18-04-ru</id>
    <published>2018-08-03T21:17:59Z</published>
    <updated>2018-08-03T21:18:36Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/apache-let-s-encrypt-ubuntu-18-04-ru"/>
    <title>Как повысить безопасность Apache с помощью Let's Encrypt в Ubuntu 18.04</title>
    <content type="html">&lt;h3 id="Введение"&gt;Введение&lt;/h3&gt;

&lt;p&gt;Let's Encrypt представляет собой центр сертификации (Certificate Authority, CA), позволяющий получать и устанавливать бесплатные &lt;a href="https://www.digitalocean.com/community/tutorials/openssl-essentials-working-with-ssl-certificates-private-keys-and-csrs"&gt;сертификаты TLS/SSL&lt;/a&gt;, тем самым позволяя использовать шифрованный HTTPS на веб-серверах. Процесс получения сертификатов упрощается за счёт наличия клиента Certbot, который пытается автоматизировать большую часть (если не все) необходимых операций. В настоящее время весь процесс получения и установки сертификатов полностью автоматизирован и для Apache и для Nginx.&lt;/p&gt;

&lt;p&gt;В этом руководстве мы используем Certbot для получения бесплатного SSL сертификата для Apache на Ubuntu 18.04, а также настроим автоматическое продление этого сертификата.&lt;/p&gt;

&lt;p&gt;В этом руководстве мы будем использовать файл отдельного виртуального хоста Apache вместо дефолтного файла конфигурации. &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-the-apache-web-server-on-ubuntu-18-04#step-5-%E2%80%94-setting-up-virtual-hosts-recommended"&gt;Мы рекомендуем&lt;/a&gt; создавать новый файлы виртуальных хостов Apache для каждого доменного имени, потому что это помогает избегать распространённых ошибок и использовать дефолтные файлы в качестве примера корректной конфигурации, когда что-нибудь пойдёт не так.&lt;/p&gt;

&lt;h2 id="Перед-установкой"&gt;Перед установкой&lt;/h2&gt;

&lt;p&gt;Перед тем, как начать следовать описанным в этой статье шагам, убедитесь, что у вас есть: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Сервер с Ubuntu 18.04, настроенный согласно &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-18-04"&gt;руководству по первичной настройке сервера с Ubuntu 18.04&lt;/a&gt;, включая настройку не-рутового (non-root) пользователя с привилегиями &lt;code&gt;sudo&lt;/code&gt; и настройку файрвола.&lt;/li&gt;
&lt;li&gt;Зарегистрированное доменное имя. В этом руководстве мы будем использовать &lt;strong&gt;example.com&lt;/strong&gt;. Вы можете приобрести доменное имя на &lt;a href="https://namecheap.com/"&gt;Namecheap&lt;/a&gt;, получить бесплатное доменное имя на &lt;a href="http://www.freenom.com/en/index.html"&gt;Freenom&lt;/a&gt; или использовать любой другой регистратор доменных имён.&lt;/li&gt;
&lt;li&gt;Для вашего сервера настроены обе записи DNS, указанные ниже. Для их настройки вы можете использовать наше &lt;a href="https://www.digitalocean.com/community/tutorials/an-introduction-to-digitalocean-dns"&gt;введение в работу с DNS в DigitalOcean&lt;/a&gt;.

&lt;ul&gt;
&lt;li&gt;Запись &lt;code&gt;A&lt;/code&gt; для &lt;code&gt;example.com&lt;/code&gt;, указывающая на публичный IP адрес вашего сервера.&lt;/li&gt;
&lt;li&gt;Запись &lt;code&gt;A&lt;/code&gt; для &lt;code&gt;www.&lt;span class="highlight"&gt;example.com&lt;/span&gt;&lt;/code&gt;, указывающая на публичный IP адрес вашего сервера.&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;Apache, установленный согласно инструкциям из руководства &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-the-apache-web-server-on-ubuntu-18-04"&gt;Как установить Apache в Ubuntu 18.04&lt;/a&gt;. Убедитесь, что у вас есть настроенный &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-the-apache-web-server-on-ubuntu-18-04#step-5-%E2%80%94-setting-up-virtual-hosts-recommended"&gt;файл виртуального хоста&lt;/a&gt; для вашего домена. В этом руководстве мы будем использовать &lt;code&gt;/etc/apache2/sites-available/&lt;span class="highlight"&gt;example.com&lt;/span&gt;.conf&lt;/code&gt; в качестве примера.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="Шаг-1-Установка-certbot"&gt;Шаг 1 - Установка Certbot&lt;/h2&gt;

&lt;p&gt;Перед началом использования Let's Encrypt для получения SSL сертификаты установим Certbot на ваш сервер.&lt;/p&gt;

&lt;p&gt;Certbot находится в активной разработке, поэтому пакеты Certbot, предоставляемые Ubuntu, обычно являются устаревшими. Тем не менее, разработчики Certbot поддерживают свой репозиторий пакетов для Ubuntu с актуальными версиями, поэтому мы будем использовать именно этот репозиторий.&lt;/p&gt;

&lt;p&gt;Сначала добавим репозиторий:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo add-apt-repository ppa:certbot/certbot
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Далее нажмите &lt;code&gt;ENTER&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Установим пакет Certbot для Apache с помощью &lt;code&gt;apt&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install python-certbot-apache
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Теперь Certbot готов к использованию, но для того, чтобы он мог настроить SSL для Apache, нам сперва необходимо проверить кое-какие настройки Apache.&lt;/p&gt;

&lt;h2 id="Шаг-2-Настройка-ssl-сертификата"&gt;Шаг 2 - Настройка SSL сертификата&lt;/h2&gt;

&lt;p&gt;Certbot должен иметь возможность найти корректный виртуальный хост в вашей конфигурации Apache для того, чтобы автоматически конфигурировать SSL. Для этого он будет искать директиву &lt;code&gt;ServerName&lt;/code&gt;, которая совпадает с доменным именем, для которого вы запросите сертификат.&lt;/p&gt;

&lt;p&gt;Если вы следовали инструкциям по &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-the-apache-web-server-on-ubuntu-18-04#step-5-%E2%80%94-setting-up-virtual-hosts-recommended"&gt;настройке виртуального хоста в руководстве по установке Apache&lt;/a&gt;, у вас должен быть виртуальный хост для вашего домена по адресу &lt;code&gt;/etc/apache2/sites-available/&lt;span class="highlight"&gt;example.com&lt;/span&gt;.conf&lt;/code&gt; с уже правильно настроенной директивой &lt;code&gt;ServerName&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Для проверки откройте файл серверного блока в &lt;code&gt;nano&lt;/code&gt; или любом другом текстовом редакторе:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/apache2/sites-available/&lt;span class="highlight"&gt;example.com&lt;/span&gt;.conf
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Найдите строку с &lt;code&gt;ServerName&lt;/code&gt;. Она должна выглядеть примерно так:&lt;/p&gt;
&lt;div class="code-label " title="/etc/apache2/sites-available/example.com.conf"&gt;/etc/apache2/sites-available/example.com.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;...
ServerName &lt;span class="highlight"&gt;example.com&lt;/span&gt;;
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Если она выглядит таким образом, закройте файл и переходите к следующему шагу.&lt;/p&gt;

&lt;p&gt;Если она не выглядит так, как описано выше, обновите директиву &lt;code&gt;ServerName&lt;/code&gt;. Затем сохраните и закройте файл, после чего проверьте корректность синтаксиса вашего конфигурационного файла командой:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apache2ctl configtest
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Если вы получили ошибку, откройте файл серверного блока и проверьте его на наличие опечаток или пропущенных символов. После того, как ваш конфигурационный файл будет проходить проверку на корректность, перезагрузите Apache для применения новой конфигурации:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl reload apache2
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Теперь Certbot может находить и обновлять корректный виртуальный хост.&lt;/p&gt;

&lt;p&gt;Далее обновим настройки файрвола для пропуска HTTPS трафика.&lt;/p&gt;

&lt;h2 id="Шаг-3-Разрешение-https-в-файрволе"&gt;Шаг 3 - Разрешение HTTPS в файрволе&lt;/h2&gt;

&lt;p&gt;Если у вас включен файрвол &lt;code&gt;ufw&lt;/code&gt;, как рекомендуется в руководстве по первичной настройке сервера, вам необходимо внести некоторые изменения в его настройки для разрешения трафика HTTPS. К счастью, Apache регистрирует необходимые профили в &lt;code&gt;ufw&lt;/code&gt; в момент установки.&lt;/p&gt;

&lt;p&gt;Вы можете ознакомиться с текущими настройками командой:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw status
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Скорее всего вывод будет выглядеть следующим образом:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Вывод"&gt;Вывод&lt;/div&gt;Status: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere                  
Apache                     ALLOW       Anywhere                  
OpenSSH (v6)               ALLOW       Anywhere (v6)             
Apache (v6)                ALLOW       Anywhere (v6)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Как видно из вывода, разрешён только трафик HTTP.&lt;/p&gt;

&lt;p&gt;Для того, чтобы разрешить трафик HTTPS, разрешим профиль &lt;code&gt;Apache Full&lt;/code&gt; и удалим избыточный профиль &lt;code&gt;Apache&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw allow 'Apache Full'
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo ufw delete allow 'Apache'
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Проверим внесённые изменения:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw status
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Теперь настройки &lt;code&gt;ufw&lt;/code&gt; должны выглядеть следующим образом:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Вывод"&gt;Вывод&lt;/div&gt;Status: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere                  
Apache Full                ALLOW       Anywhere                  
OpenSSH (v6)               ALLOW       Anywhere (v6)             
Apache Full (v6)           ALLOW       Anywhere (v6)  
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Теперь мы можем запустить Certbot и получить наши сертификаты.&lt;/p&gt;

&lt;h2 id="Шаг-4-Получение-ssl-сертификата"&gt;Шаг 4 - Получение SSL сертификата&lt;/h2&gt;

&lt;p&gt;Certbot предоставляет несколько способов получения сертификатов SSL с использованием плагинов. Плагин для Apache берёт на себя настройку Apache и перезагрузку конфигурации, когда это необходимо. Для использования плагина выполним команду:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo certbot --apache -d &lt;span class="highlight"&gt;example.com&lt;/span&gt; -d www.&lt;span class="highlight"&gt;example.com&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Эта команда запускает &lt;code&gt;certbot&lt;/code&gt; с плагином &lt;code&gt;--apache&lt;/code&gt;, ключи &lt;code&gt;-d&lt;/code&gt; определяют имена доменов, для которых должен быть выпущен сертификат.&lt;/p&gt;

&lt;p&gt;Если это первый раз, когда вы запускаете &lt;code&gt;certbot&lt;/code&gt;, вам будет предложено ввести адрес электронной почты и согласиться с условиями использования сервиса. После этого &lt;code&gt;certbot&lt;/code&gt; свяжется с сервером Let's Encrypt, а затем проверит, что вы действительно контролируете домен, для которого вы запросили сертификат.&lt;/p&gt;

&lt;p&gt;Если всё прошло успешно, &lt;code&gt;certbot&lt;/code&gt; спросит, как вы хотите настроить конфигурацию HTTPS.&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Вывод"&gt;Вывод&lt;/div&gt;Please choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access.
-------------------------------------------------------------------------------
1: No redirect - Make no further changes to the webserver configuration.
2: Redirect - Make all requests redirect to secure HTTPS access. Choose this for
new sites, or if you're confident your site works on HTTPS. You can undo this
change by editing your web server's configuration.
-------------------------------------------------------------------------------
Select the appropriate number [1-2] then [enter] (press 'c' to cancel):
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Выберите подходящий вариант и нажмите &lt;code&gt;ENTER&lt;/code&gt;. Конфигурация будет обновлена, а Apache перезапущен для применения изменений. &lt;code&gt;certbot&lt;/code&gt; выдаст сообщение о том, что процесс прошёл успешно, и где хранятся ваши сертификаты:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Вывод"&gt;Вывод&lt;/div&gt;IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/&lt;span class="highlight"&gt;example.com&lt;/span&gt;/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/&lt;span class="highlight"&gt;example.com&lt;/span&gt;/privkey.pem
   Your cert will expire on 2018-07-23. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot again
   with the "certonly" option. To non-interactively renew *all* of
   your certificates, run "certbot renew"
 - Your account credentials have been saved in your Certbot
   configuration directory at /etc/letsencrypt. You should make a
   secure backup of this folder now. This configuration directory will
   also contain certificates and private keys obtained by Certbot so
   making regular backups of this folder is ideal.
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ваши сертификаты загружены, установлены и работают. Попробуйте перезагрузить ваш сайт с использованием &lt;code&gt;https://&lt;/code&gt; и вы увидите значок безопасности в браузере. Он означает, что соединение с сайтом зашифровано, обычно он выглядит, как зелёная иконка замка. Если вы проверите ваш сервер тестом &lt;a href="https://www.ssllabs.com/ssltest/"&gt;SSL Labs Server Test&lt;/a&gt;, он получит оценку &lt;strong&gt;A&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Закончим тестированием процесса обновления сертификата.&lt;/p&gt;

&lt;h2 id="Шаг-5-Проверка-автоматического-обновления-сертификата"&gt;Шаг 5 - Проверка автоматического обновления сертификата&lt;/h2&gt;

&lt;p&gt;Сертификаты Let's Encrypt действительны только 90 дней. Это сделано для того, чтобы пользователи автоматизировали процесс обновления сертификатов. Пакет &lt;code&gt;certbot&lt;/code&gt;, который мы установили, делает это путём добавления скрипта обновления в &lt;code&gt;/etc/cron.d&lt;/code&gt;. Этот скрипт запускается раз в день и автоматически обновляет любые сертификаты, которые закончатся в течение ближайших 30 дней.&lt;/p&gt;

&lt;p&gt;Для тестирования процесса обновления мы можем сделать "сухой" запуск (dry run) &lt;code&gt;certbot&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo certbot renew --dry-run
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Если вы не видите каких-либо ошибок в результате выполнения этой команды, то всё в полном порядке. При необходимости Certbot будет обновлять ваши сертификаты и перезагружать Apache для применения изменений. Если автоматическое обновление по какой-либо причине закончится ошибкой, Let's Encrypt отправит электронное письмо на указанный вами адрес электронной почты с информацией о сертификате, который скоро закончится.&lt;/p&gt;

&lt;h2 id="Заключение"&gt;Заключение&lt;/h2&gt;

&lt;p&gt;В этом руководстве мы рассмотрели процесс установки клиента Let's Encrypt &lt;code&gt;certbot&lt;/code&gt;, загрузили SSL сертификаты для вашего домена, настроили Apache для использования этих сертификатов и настроили процесс автоматического обновления сертификатов. Если у вас есть вопросы по работе с Certbot, рекомендуем ознакомиться с &lt;a href="https://certbot.eff.org/docs/"&gt;документацией Certbot&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-use-object-methods-in-javascript</id>
    <published>2018-08-03T17:42:52Z</published>
    <updated>2018-08-03T17:47:34Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-use-object-methods-in-javascript"/>
    <title>How To Use Object Methods in JavaScript</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.digitalocean.com/community/tutorials/understanding-objects-in-javascript"&gt;Objects&lt;/a&gt; in JavaScript are collections of &lt;strong&gt;key&lt;/strong&gt;/&lt;strong&gt;value&lt;/strong&gt; pairs. The values can consist of &lt;strong&gt;properties&lt;/strong&gt; and &lt;strong&gt;methods&lt;/strong&gt;, and may contain all other JavaScript data types, such as strings, numbers, and Booleans.&lt;/p&gt;

&lt;p&gt;All objects in JavaScript descend from the parent &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object"&gt;&lt;code&gt;Object&lt;/code&gt;&lt;/a&gt; constructor. &lt;code&gt;Object&lt;/code&gt; has many useful built-in methods we can use and access to make working with individual objects straightforward. Unlike &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-use-array-methods-in-javascript-mutator-methods"&gt;Array prototype methods&lt;/a&gt; like &lt;code&gt;sort()&lt;/code&gt; and &lt;code&gt;reverse()&lt;/code&gt; that are used on the array instance, Object methods are used directly on the &lt;code&gt;Object&lt;/code&gt; constructor, and use the object instance as a parameter. This is known as a static method.&lt;/p&gt;

&lt;p&gt;This tutorial will go over important built-in object methods, with each section below dealing with a specific method and providing an example of use.&lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;In order to get the most out of this tutorial, you should be familiar with creating, modifying, and working with objects, which you can review in the "&lt;a href="https://www.digitalocean.com/community/tutorials/understanding-objects-in-javascript"&gt;Understanding Objects in JavaScript&lt;/a&gt;" article.&lt;/p&gt;

&lt;p&gt;For additional guidance on JavaScript in general, you can review our &lt;em&gt;&lt;a href="https://www.digitalocean.com/community/tutorial_series/how-to-code-in-javascript"&gt;How To Code in JavaScript&lt;/a&gt;&lt;/em&gt; series.&lt;/p&gt;

&lt;h2 id="object-create"&gt;Object.create()&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create"&gt;&lt;code&gt;Object.create()&lt;/code&gt;&lt;/a&gt; method is used to create a new object and link it to the prototype of an existing object.&lt;/p&gt;

&lt;p&gt;We can create a &lt;code&gt;job&lt;/code&gt; object instance, and extend it to a more specific object.&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-js"&gt;// Initialize an object with properties and methods
const job = {
    position: 'cashier',
    type: 'hourly',
    isAvailable: true,
    showDetails() {
        const accepting = this.isAvailable ? 'is accepting applications' : "is not currently accepting applications";

        console.log(`The ${this.position} position is ${this.type} and ${accepting}.`);
    }
};

// Use Object.create to pass properties
const barista = Object.create(job);

barista.position = "barista";
barista.showDetails();
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;The barista position is hourly and is accepting applications.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;barista&lt;/code&gt; object now has one property — &lt;code&gt;position&lt;/code&gt; — but all the other properties and methods from &lt;code&gt;job&lt;/code&gt; are available through the prototype. &lt;code&gt;Object.create()&lt;/code&gt; is useful for keeping code &lt;a href="https://www.digitalocean.com/community/tutorials/digitalocean-community-glossary#dry-development"&gt;DRY&lt;/a&gt; by minimizing duplication.&lt;/p&gt;

&lt;h2 id="object-keys"&gt;Object.keys()&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys"&gt;&lt;code&gt;Object.keys()&lt;/code&gt;&lt;/a&gt; creates an array containing the keys of an object.&lt;/p&gt;

&lt;p&gt;We can create an object and print the array of keys.&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-js"&gt;// Initialize an object
const employees = {
    boss: 'Michael',
    secretary: 'Pam',
    sales: 'Jim',
    accountant: 'Oscar'
};

// Get the keys of the object
const keys = Object.keys(employees);

console.log(keys);
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;["boss", "secretary", "sales", "accountant"]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;Object.keys&lt;/code&gt; can be used to iterate through the keys and values of an object.&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-js"&gt;// Iterate through the keys
Object.keys(employees).forEach(key =&amp;gt; {
    let value = employees[key];

     console.log(`${key}: ${value}`);
});
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;boss: Michael
secretary: Pam
sales: Jim
accountant: Oscar
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;Object.keys&lt;/code&gt; is also useful for checking the length of an object.&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-js"&gt;// Get the length of the keys
const length = Object.keys(employees).length;

console.log(length);
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;4
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Using the &lt;code&gt;length&lt;/code&gt; property, we were able to count the &lt;code&gt;4&lt;/code&gt; properties of &lt;code&gt;employees&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id="object-values"&gt;Object.values()&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/values"&gt;&lt;code&gt;Object.values()&lt;/code&gt;&lt;/a&gt; creates an array containing the values of an object.&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-js"&gt;// Initialize an object
const session = {
    id: 1,
    time: `26-July-2018`,
    device: 'mobile',
    browser: 'Chrome'
};

// Get all values of the object
const values = Object.values(session);

console.log(values);
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;[1, "26-July-2018", "mobile", "Chrome"]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;Object.keys()&lt;/code&gt; and &lt;code&gt;Object.values()&lt;/code&gt; allow you to return the data from an object.&lt;/p&gt;

&lt;h2 id="object-entries"&gt;Object.entries()&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries"&gt;&lt;code&gt;Object.entries()&lt;/code&gt;&lt;/a&gt; creates a nested array of the key/value pairs of an object.&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-js"&gt;// Initialize an object
const operatingSystem = {
    name: 'Ubuntu',
    version: 18.04,
    license: 'Open Source'
};

// Get the object key/value pairs
const entries = Object.entries(operatingSystem);

console.log(entries);
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;[
    ["name", "Ubuntu"]
    ["version", 18.04]
    ["license", "Open Source"]
]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once we have the key/value pair arrays, we can use the &lt;code&gt;forEach()&lt;/code&gt; method to loop through and work with the results.&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-js"&gt;// Loop through the results
entries.forEach(entry =&amp;gt; {
    let key = entry[0];
    let value = entry[1];

    console.log(`${key}: ${value}`);
});
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;name: Ubuntu
version: 18.04
license: Open Source
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;Object.entries()&lt;/code&gt; method will only return the object instance's own properties, and not any properties that may be inherited through its prototype.&lt;/p&gt;

&lt;h2 id="object-assign"&gt;Object.assign()&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign"&gt;&lt;code&gt;Object.assign()&lt;/code&gt;&lt;/a&gt; is used to copy values from one object to another.&lt;/p&gt;

&lt;p&gt;We can create two objects, and merge them with &lt;code&gt;Object.assign()&lt;/code&gt;.&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-js"&gt;// Initialize an object
const name = {
    firstName: 'Philip',
    lastName: 'Fry'
};

// Initialize another object
const details = {
    job: 'Delivery Boy',
    employer: 'Planet Express'
};

// Merge the objects
const character = Object.assign(name, details);

console.log(character);
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;{firstName: "Philip", lastName: "Fry", job: "Delivery Boy", employer: "Planet Express"}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It is also possible to use the spread operator (&lt;code&gt;...&lt;/code&gt;) to accomplish the same task. In the code below, we'll modify how we declare &lt;code&gt;character&lt;/code&gt; through merging the &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;details&lt;/code&gt; objects.&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-js"&gt;// Initialize an object
const name = {
    firstName: 'Philip',
    lastName: 'Fry'
};

// Initialize another object
const details = {
    job: 'Delivery Boy',
    employer: 'Planet Express'
};

// Merge the object with the spread operator
const character = {...name, ...details}

console.log(character);
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;{firstName: "Philip", lastName: "Fry", job: "Delivery Boy", employer: "Planet Express"}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals"&gt;spread syntax&lt;/a&gt; in object literals is also known as shallow-cloning.&lt;/p&gt;

&lt;h2 id="object-freeze"&gt;Object.freeze()&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze"&gt;&lt;code&gt;Object.freeze()&lt;/code&gt;&lt;/a&gt; prevents modification to properties and values of an object, and prevents properties from being added or removed from an object.&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-js"&gt;// Initialize an object
const user = {
    username: 'AzureDiamond',
    password: 'hunter2'
};

// Freeze the object
const newUser = Object.freeze(user);

newUser.password = '*******';
newUser.active = true;

console.log(newUser);
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;{username: "AzureDiamond", password: "hunter2"}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the example above, we tried to override the password &lt;code&gt;hunter2&lt;/code&gt; with &lt;code&gt;*******&lt;/code&gt;, but the &lt;code&gt;password&lt;/code&gt; property remained the same. We also tried to add a new property, &lt;code&gt;active&lt;/code&gt;, but it was not added.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Object.isFrozen()&lt;/code&gt; is available to determine whether an object has been frozen or not, and returns a Boolean.&lt;/p&gt;

&lt;h2 id="object-seal"&gt;Object.seal()&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/seal"&gt;&lt;code&gt;Object.seal()&lt;/code&gt;&lt;/a&gt; prevents new properties from being added to an object, but allows the modification of existing properties. This method is similar to &lt;code&gt;Object.freeze()&lt;/code&gt;. Refresh your console before implementing the code below to avoid an error.&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-js"&gt;// Initialize an object
const user = {
    username: 'AzureDiamond',
    password: 'hunter2'
};

// Seal the object
const newUser = Object.seal(user);

newUser.password = '*******';
newUser.active = true;

console.log(newUser);
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;{username: "AzureDiamond", password: "*******"}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The new &lt;code&gt;active&lt;/code&gt; property was not added to the sealed object, but the &lt;code&gt;password&lt;/code&gt; property was successfully changed.&lt;/p&gt;

&lt;h2 id="object-getprototypeof"&gt;Object.getPrototypeOf()&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getPrototypeOf"&gt;&lt;code&gt;Object.getPrototypeOf()&lt;/code&gt;&lt;/a&gt; is used to get the internal hidden &lt;code&gt;[[Prototype]]&lt;/code&gt; of an object, also accessible through the &lt;code&gt;__proto__&lt;/code&gt; property.&lt;/p&gt;

&lt;p&gt;In this example, we can create an array, which has access to the &lt;code&gt;Array&lt;/code&gt; prototype.&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-js"&gt;const employees = ['Ron', 'April', 'Andy', 'Leslie'];

Object.getPrototypeOf(employees);
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;[constructor: ƒ, concat: ƒ, find: ƒ, findIndex: ƒ, pop: ƒ, …]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can see in the output that the prototype of the &lt;code&gt;employees&lt;/code&gt; array has access to &lt;code&gt;pop&lt;/code&gt;, &lt;code&gt;find&lt;/code&gt;, and other Array prototype methods. We can confirm this by testing the &lt;code&gt;employees&lt;/code&gt; prototype against &lt;code&gt;Array.prototype&lt;/code&gt;.&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;Object.getPrototypeOf(employees) === Array.prototype;
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This method can be useful to get more information about an object or ensure it has access to the prototype of another object.&lt;/p&gt;

&lt;p&gt;There is also a related &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/setPrototypeOf"&gt;&lt;code&gt;Object.setPrototypeOf()&lt;/code&gt;&lt;/a&gt; method that will add one prototype to another object. It is recommended that you use &lt;code&gt;Object.create()&lt;/code&gt; instead as it is faster and more performant.&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Objects have many useful methods that help us modify, protect, and iterate through them. In this tutorial, we reviewed how to create and assign new objects, iterate through the keys and/or values of an object, and freeze or seal an object.&lt;/p&gt;

&lt;p&gt;If you need to review JavaScript objects you can read "&lt;a href="https://www.digitalocean.com/community/tutorials/understanding-objects-in-javascript"&gt;Understanding Objects in JavaScript&lt;/a&gt;." If you would like to familiarize yourself with the prototype chain, you can take a look at "&lt;a href="https://www.digitalocean.com/community/tutorials/understanding-prototypes-and-inheritance-in-javascript"&gt;Understanding Prototypes and Inheritance in JavaScript&lt;/a&gt;."&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/using-a-cdn-to-speed-up-static-content-delivery</id>
    <published>2018-07-09T21:58:54Z</published>
    <updated>2018-08-06T19:22:46Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/using-a-cdn-to-speed-up-static-content-delivery"/>
    <title>Using a CDN to Speed Up Static Content Delivery</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;Modern websites and applications must often deliver a significant amount of static content to end users. This content includes images, stylesheets, JavaScript, and video. As these static assets grow in number and size, bandwidth usage swells and page load times increase, deteriorating the browsing experience for your users and reducing your servers' available capacity. &lt;/p&gt;

&lt;p&gt;To dramatically reduce page load times, improve performance, and reduce your bandwidth and infrastructure costs, you can implement a CDN, or &lt;strong&gt;c&lt;/strong&gt;ontent &lt;strong&gt;d&lt;/strong&gt;elivery &lt;strong&gt;n&lt;/strong&gt;etwork, to cache these assets across a set of geographically distributed servers.&lt;/p&gt;

&lt;p&gt;In this tutorial, we’ll provide a high-level overview of CDNs and how they work, as well as the benefits they can provide for your web applications.&lt;/p&gt;

&lt;h2 id="what-is-a-cdn"&gt;What is a CDN?&lt;/h2&gt;

&lt;p&gt;A content delivery network is a geographically distributed group of servers optimized to deliver static content to end users. This static content can be almost any sort of data, but CDNs are most commonly used to deliver web pages and their related files, streaming video and audio, and large software packages.&lt;/p&gt;

&lt;p&gt;&lt;img src="http://assets.digitalocean.com/articles/CDN/without-CDN.png" alt="Diagram of content delivery without a CDN"&gt;&lt;/p&gt;

&lt;p&gt;A CDN consists of multiple &lt;em&gt;points of presence&lt;/em&gt; (PoPs) in various locations, each consisting of several &lt;em&gt;edge&lt;/em&gt; servers that cache assets from your &lt;em&gt;origin&lt;/em&gt;, or host server. When a user visits your website and requests static assets like images or JavaScript files, their requests are routed by the CDN to the nearest edge server, from which the content is served. If the edge server does not have the assets cached or the cached assets have expired, the CDN will fetch and cache the latest version from either another nearby CDN edge server or your origin servers. If the CDN edge does have a cache entry for your assets (which occurs the majority of the time if your website receives a moderate amount of traffic), it will return the cached copy to the end user.&lt;/p&gt;

&lt;p&gt;&lt;img src="http://assets.digitalocean.com/articles/CDN/CDN.png" alt="Content Delivery Network (CDN) diagram"&gt;&lt;/p&gt;

&lt;p&gt;This allows geographically dispersed users to minimize the number of hops needed to receive static content, fetching the content directly from a nearby edge's cache. The result is significantly decreased latencies and packet loss, faster page load times, and drastically reduced load on your origin infrastructure.&lt;/p&gt;

&lt;p&gt;CDN providers often offer additional features such as &lt;a href="https://www.digitalocean.com/community/tutorials/digitalocean-community-glossary#ddos-attack"&gt;DDoS&lt;/a&gt; mitigation and rate-limiting, user analytics, and optimizations for streaming or mobile use cases at additional cost.&lt;/p&gt;

&lt;h2 id="how-does-a-cdn-work"&gt;How Does a CDN Work?&lt;/h2&gt;

&lt;p&gt;When a user visits your website, they first receive a response from a DNS server containing the IP address of your host web server. Their browser then requests the web page content, which often consists of a variety of static files, such as HTML pages, CSS stylesheets, JavaScript code, and images.&lt;/p&gt;

&lt;p&gt;Once you roll out a CDN and offload these static assets onto CDN servers, either by "pushing" them out manually or having the CDN "pull" the assets automatically (both mechanisms are covered in the &lt;a href="https://www.digitalocean.com/community/tutorials/using-a-cdn-to-speed-up-static-content-delivery#push-vs-pull-zones"&gt;next section&lt;/a&gt;), you then instruct your web server to rewrite links to static content such that these links now point to files hosted by the CDN. If you're using a CMS such as WordPress, this link rewriting can be implemented using a third-party plugin like &lt;a href="https://wordpress.org/plugins/cdn-enabler/"&gt;CDN Enabler&lt;/a&gt;.  &lt;/p&gt;

&lt;p&gt;Many CDNs provide support for custom domains, allowing you to create a CNAME record under your domain pointing to a CDN endpoint. Once the CDN receives a user request at this endpoint (located at the edge, much closer to the user than your backend servers), it then routes the request to the Point of Presence (PoP) located closest to the user. This PoP often consists of one or more CDN edge servers collocated at an Internet Exchange Point (IxP), essentially a data center that Internet Service Providers (ISPs) use to interconnect their networks. The CDN's internal load balancer then routes the request to an edge server located at this PoP, which then serves the content to the user.&lt;/p&gt;

&lt;p&gt;Caching mechanisms vary across CDN providers, but generally they work as follows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;When the CDN receives a first request for a static asset, such as a PNG image, it does not have the asset cached and must fetch a copy of the asset from either a nearby CDN edge server, or the origin server itself. This is known as a cache "miss," and can usually be detected by inspecting the HTTP response header, containing &lt;code&gt;X-Cache: MISS&lt;/code&gt;. This initial request will be slower than future requests because after completing this request the asset will have been cached at the edge.&lt;/li&gt;
&lt;li&gt;Future requests for this asset (cache "hits"), routed to this edge location, will now be served from cache, until expiry (usually set through HTTP headers). These responses will be significantly faster than the initial request, dramatically reducing latencies for users and offloading web traffic onto the CDN network. You can verify that the response was served from a CDN cache by inspecting the HTTP response header, which should now contain &lt;code&gt;X-Cache: HIT&lt;/code&gt;.&lt;br&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To learn more about how a specific CDN works and has been implemented, consult your CDN provider’s documentation. &lt;/p&gt;

&lt;p&gt;In the next section, we’ll introduce the two popular types of CDNs: &lt;strong&gt;push&lt;/strong&gt; and &lt;strong&gt;pull&lt;/strong&gt; CDNs.&lt;/p&gt;

&lt;h2 id="push-vs-pull-zones"&gt;Push vs. Pull Zones&lt;/h2&gt;

&lt;p&gt;Most CDN providers offer two ways of caching your data: pull zones and push zones. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pull Zones&lt;/strong&gt; involve entering your origin server's address, and letting the CDN automatically fetch and cache all the static resources available on your site. Pull zones are commonly used to deliver frequently updated, small to medium sized web assets like HTML, CSS, and JavaScript files. After providing the CDN with your origin server's address, the next step is usually rewriting links to static assets such that they now point to the URL provided by the CDN. From that point onwards, the CDN will handle your users' incoming asset requests and serve content from its geographically distributed caches and your origin as appropriate.&lt;/p&gt;

&lt;p&gt;To use a &lt;strong&gt;Push Zone&lt;/strong&gt;, you upload your data to a designated bucket or storage location, which the CDN then pushes out to caches on its distributed fleet of edge servers. Push zones are typically used for larger, infrequently changing files, like archives, software packages, PDFs, video, and audio files.&lt;/p&gt;

&lt;h2 id="benefits-of-using-a-cdn"&gt;Benefits of Using a CDN&lt;/h2&gt;

&lt;p&gt;Almost any site can reap the benefits provided by rolling out a CDN, but generally the core reasons for implementing one are to offload bandwidth from your origin servers onto the CDN servers, and to reduce latency for geographically distributed users.&lt;/p&gt;

&lt;p&gt;We’ll go through these and several of the other major advantages afforded by using a CDN below.&lt;/p&gt;

&lt;h3 id="origin-offload"&gt;Origin Offload&lt;/h3&gt;

&lt;p&gt;If you're nearing bandwidth capacity on your servers, offloading static assets like images, videos, CSS and JavaScript files will drastically reduce your servers' bandwidth usage. Content delivery networks are designed and optimized for serving static content, and client requests for this content will be routed to and served by edge CDN servers. This has the added benefit of reducing load on your origin servers, as they then serve this data at a much lower frequency.&lt;/p&gt;

&lt;h3 id="lower-latency-for-improved-user-experience"&gt;Lower Latency for Improved User Experience&lt;/h3&gt;

&lt;p&gt;If your user base is geographically dispersed, and a non-trivial portion of your traffic comes from a distant geographical area, a CDN can decrease latency by caching static assets on edge servers closer to your users. By reducing the distance between your users and static content, you can more quickly deliver content to your users and improve their experience by boosting page load speeds. &lt;/p&gt;

&lt;p&gt;These benefits are compounded for websites serving primarily bandwidth-intensive video content, where high latencies and slow loading times more directly impact user experience and content engagement.&lt;/p&gt;

&lt;h3 id="manage-traffic-spikes-and-avoid-downtime"&gt;Manage Traffic Spikes and Avoid Downtime&lt;/h3&gt;

&lt;p&gt;CDNs allow you to handle large traffic spikes and bursts by load balancing requests across a large, distributed network of edge servers. By offloading and caching static content on a delivery network, you can accommodate a larger number of simultaneous users with your existing infrastructure. &lt;/p&gt;

&lt;p&gt;For websites using a single origin server, these large traffic spikes can often overwhelm the system, causing unplanned outages and downtime. Shifting traffic onto highly available and redundant CDN infrastructure, designed to handle variable levels of web traffic, can increase the availability of your assets and content.&lt;/p&gt;

&lt;h3 id="reduce-costs"&gt;Reduce Costs&lt;/h3&gt;

&lt;p&gt;As serving static content usually makes up the majority of your bandwidth usage, offloading these assets onto a content delivery network can drastically reduce your monthly infrastructure spend. In addition to reducing bandwidth costs, a CDN can decrease server costs by reducing load on the origin servers, enabling your existing infrastructure to scale. Finally, some CDN providers offer fixed-price monthly billing, allowing you to transform your variable monthly bandwidth usage into a stable, predictable recurring spend.   &lt;/p&gt;

&lt;h3 id="increase-security"&gt;Increase Security&lt;/h3&gt;

&lt;p&gt;Another common use case for CDNs is DDoS attack mitigation. Many CDN providers include features to monitor and filter requests to edge servers. These services analyze web traffic for suspicious patterns, blocking malicious attack traffic while continuing to allow reputable user traffic through. CDN providers usually offer a variety of DDoS mitigation services, from common attack protection at the infrastructure level (&lt;a href="https://en.wikipedia.org/wiki/Denial-of-service_attack#Types"&gt;OSI layers 3 and 4&lt;/a&gt;), to more advanced mitigation services and rate limiting. &lt;/p&gt;

&lt;p&gt;In addition, most CDNs let you configure full SSL, so that you can encrypt traffic between the CDN and the end user, as well as traffic between the CDN and your origin servers, using either CDN-provided or custom SSL certificates. &lt;/p&gt;

&lt;h2 id="choosing-the-best-solution"&gt;Choosing the Best Solution&lt;/h2&gt;

&lt;p&gt;If your bottleneck is CPU load on the origin server, and not bandwidth, a CDN may not be the most appropriate solution. In this case, local caching using popular caches such as NGINX or Varnish may significantly reduce load by serving assets from system memory.&lt;/p&gt;

&lt;p&gt;Before rolling out a CDN, additional optimization steps — like minifying and compressing JavaScript and CSS files, and enabling web server HTTP request compression — can also have a significant impact on page load times and bandwidth usage. &lt;/p&gt;

&lt;p&gt;A helpful tool to measure your page load speed and improve it is Google's &lt;a href="https://developers.google.com/speed/pagespeed/insights/"&gt;PageSpeed Insights&lt;/a&gt;. Another helpful tool that provides a waterfall breakdown of request and response times as well as suggested optimizations is &lt;a href="https://www.pingdom.com/"&gt;Pingdom&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;A content delivery network can be a quick and effective solution for improving the scalability and availability of your web sites. By caching static assets on a geographically distributed network of optimized servers, you can greatly reduce page load times and latencies for end users. In addition, CDNs allow you to significantly reduce your bandwidth usage by absorbing user requests and responding from cache at the edge, thus lowering your bandwidth and infrastructure costs. &lt;/p&gt;

&lt;p&gt;With plugins and third-party support for major frameworks like WordPress, Drupal, Django, and Ruby on Rails, as well as additional features like DDoS mitigation, full SSL, user monitoring, and asset compression, CDNs can be an impactful tool for securing and optimizing high-traffic web sites. &lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-create-a-kubernetes-1-11-cluster-using-kubeadm-on-ubuntu-18-04</id>
    <published>2018-07-31T20:16:48Z</published>
    <updated>2018-09-19T19:14:31Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-create-a-kubernetes-1-11-cluster-using-kubeadm-on-ubuntu-18-04"/>
    <title>How To Create a Kubernetes 1.11 Cluster Using Kubeadm on Ubuntu 18.04</title>
    <content type="html">&lt;p&gt;&lt;em&gt;The author selected the &lt;a href="https://www.brightfunds.org/funds/foss-nonprofits"&gt;Free and Open Source Fund&lt;/a&gt; to receive a donation as part of the &lt;a href="https://do.co/w4do-cta"&gt;Write for DOnations&lt;/a&gt; program.&lt;/em&gt;&lt;/p&gt;

&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://kubernetes.io"&gt;Kubernetes&lt;/a&gt; is a container orchestration system that manages containers at scale. Initially developed by Google based on its experience running containers in production, Kubernetes is open source and actively developed by a community around the world.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://kubernetes.io/docs/reference/setup-tools/kubeadm/kubeadm/"&gt;Kubeadm&lt;/a&gt; automates the installation and configuration of Kubernetes components such as the API server, Controller Manager, and Kube DNS. It does not, however, create users or handle the installation of operating-system-level dependencies and their configuration. For these preliminary tasks, it is possible to use a configuration management tool like &lt;a href="https://www.ansible.com/"&gt;Ansible&lt;/a&gt; or &lt;a href="https://saltstack.com/"&gt;SaltStack&lt;/a&gt;. Using these tools makes creating additional clusters or recreating existing clusters much simpler and less error prone.  &lt;/p&gt;

&lt;p&gt;In this guide, you will set up a Kubernetes cluster from scratch using Ansible and Kubeadm, and then deploy a containerized Nginx application to it.&lt;/p&gt;

&lt;h2 id="goals"&gt;Goals&lt;/h2&gt;

&lt;p&gt;Your cluster will include the following physical resources: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;One master node&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The master node (a &lt;em&gt;node&lt;/em&gt; in Kubernetes refers to a server) is responsible for managing the state of the cluster. It runs &lt;a href="https://github.com/coreos/etcd"&gt;Etcd&lt;/a&gt;, which stores cluster data among components that schedule workloads to worker nodes. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Two worker nodes&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Worker nodes are the servers where your &lt;em&gt;workloads&lt;/em&gt; (i.e. containerized applications and services) will run. A worker will continue to run your workload once they're assigned to it, even if the master goes down once scheduling is complete. A cluster's capacity can be increased by adding workers.&lt;/p&gt;

&lt;p&gt;After completing this guide, you will have a cluster ready to run containerized applications, provided that the servers in the cluster have sufficient CPU and RAM resources for your applications to consume. Almost any traditional Unix application including web applications, databases, daemons, and command line tools can be containerized and made to run on the cluster. The cluster itself will consume around 300-500MB of memory and 10% of CPU on each node. &lt;/p&gt;

&lt;p&gt;Once the cluster is set up, you will deploy the web server &lt;a href="https://nginx.org/en/"&gt;Nginx&lt;/a&gt; to it to ensure that it is running workloads correctly. &lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;An SSH key pair on your local Linux/macOS/BSD machine. If you haven't used SSH keys before, you can learn how to set them up by following &lt;a href="https://www.digitalocean.com/community/tutorials/ssh-essentials-working-with-ssh-servers-clients-and-keys#generating-and-working-with-ssh-keys"&gt;this explanation of how to set up SSH keys on your local machine&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Three servers running Ubuntu 18.04 with at least 1GB RAM. You should be able to SSH into each server as the root user with your SSH key pair. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Ansible installed on your local machine. If you're running Ubuntu 18.04 as your OS, follow the "Step 1 - Installing Ansible" section in &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-and-configure-ansible-on-ubuntu-18-04#step-1-%E2%80%94-installing-ansible"&gt;How to Install and Configure Ansible on Ubuntu 18.04&lt;/a&gt; to install Ansible. For installation instructions on other platforms like macOS or CentOS, follow the &lt;a href="http://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html#installing-the-control-machine"&gt;official Ansible installation documentation&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Familiarity with Ansible playbooks. For review, check out &lt;a href="https://www.digitalocean.com/community/tutorials/configuration-management-101-writing-ansible-playbooks"&gt;Configuration Management 101: Writing Ansible Playbooks&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Knowledge of how to launch a container from a Docker image. Look at "Step 5 — Running a Docker Container" in &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-and-use-docker-on-ubuntu-18-04#step-5-%E2%80%94-running-a-docker-container"&gt;How To Install and Use Docker on Ubuntu 18.04&lt;/a&gt; if you need a refresher. &lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="step-1-—-setting-up-the-workspace-directory-and-ansible-inventory-file"&gt;Step 1 — Setting Up the Workspace Directory and Ansible Inventory File&lt;/h2&gt;

&lt;p&gt;In this section, you will create a directory on your local machine that will serve as your workspace. You will configure Ansible locally so that it can communicate with and execute commands on your remote servers. Once that's done, you will create a &lt;code&gt;hosts&lt;/code&gt; file containing inventory information such as the IP addresses of your servers and the groups that each server belongs to. &lt;/p&gt;

&lt;p&gt;Out of your three servers, one will be the master with an IP displayed as &lt;code&gt;&lt;span class="highlight"&gt;master_ip&lt;/span&gt;&lt;/code&gt;. The other two servers will be workers and will have the IPs &lt;code&gt;&lt;span class="highlight"&gt;worker_1_ip&lt;/span&gt;&lt;/code&gt; and &lt;code&gt;&lt;span class="highlight"&gt;worker_2_ip&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Create a directory named &lt;code&gt;~/kube-cluster&lt;/code&gt; in the home directory of your local machine and &lt;code&gt;cd&lt;/code&gt; into it:&lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;mkdir ~/kube-cluster
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;cd ~/kube-cluster
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This directory will be your workspace for the rest of the tutorial and will contain all of your Ansible playbooks. It will also be the directory inside which you will run all local commands. &lt;/p&gt;

&lt;p&gt;Create a file named &lt;code&gt;~/kube-cluster/hosts&lt;/code&gt; using &lt;code&gt;nano&lt;/code&gt; or your favorite text editor: &lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nano ~/kube-cluster/hosts
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add the following text to the file, which will specify information about the logical structure of your cluster:&lt;/p&gt;
&lt;div class="code-label " title="~/kube-cluster/hosts"&gt;~/kube-cluster/hosts&lt;/div&gt;&lt;pre class="code-pre yaml local-environment"&gt;&lt;code langs=""&gt;[masters]
master ansible_host=&lt;span class="highlight"&gt;master_ip&lt;/span&gt; ansible_user=root

[workers]
worker1 ansible_host=&lt;span class="highlight"&gt;worker_1_ip&lt;/span&gt; ansible_user=root
worker2 ansible_host=&lt;span class="highlight"&gt;worker_2_ip&lt;/span&gt; ansible_user=root

[all:vars]
ansible_python_interpreter=/usr/bin/python3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You may recall that &lt;a href="http://docs.ansible.com/ansible/latest/user_guide/intro_inventory.html"&gt;&lt;em&gt;inventory files&lt;/em&gt;&lt;/a&gt; in Ansible are used to specify server information such as IP addresses, remote users, and groupings of servers to target as a single unit for executing commands. &lt;code&gt;~/kube-cluster/hosts&lt;/code&gt; will be your inventory file and you've added two Ansible groups (&lt;strong&gt;masters&lt;/strong&gt; and &lt;strong&gt;workers&lt;/strong&gt;) to it specifying the logical structure of your cluster. &lt;/p&gt;

&lt;p&gt;In the &lt;strong&gt;masters&lt;/strong&gt; group, there is a server entry named "master" that lists the master node's IP (&lt;code&gt;&lt;span class="highlight"&gt;master_ip&lt;/span&gt;&lt;/code&gt;) and specifies that Ansible should run remote commands as the root user. &lt;/p&gt;

&lt;p&gt;Similarly, in the &lt;strong&gt;workers&lt;/strong&gt; group, there are two entries for the worker servers (&lt;code&gt;&lt;span class="highlight"&gt;worker_1_ip&lt;/span&gt;&lt;/code&gt; and &lt;code&gt;&lt;span class="highlight"&gt;worker_2_ip&lt;/span&gt;&lt;/code&gt;) that also specify the &lt;code&gt;ansible_user&lt;/code&gt; as root.&lt;/p&gt;

&lt;p&gt;The last line of the file tells Ansible to use the remote servers' Python 3 interpreters for its management operations. &lt;/p&gt;

&lt;p&gt;Save and close the file after you've added the text.&lt;/p&gt;

&lt;p&gt;Having set up the server inventory with groups, let's move on to installing operating system level dependencies and creating configuration settings.&lt;/p&gt;

&lt;h2 id="step-2-—-creating-a-non-root-user-on-all-remote-servers"&gt;Step 2 — Creating a Non-Root User on All Remote Servers&lt;/h2&gt;

&lt;p&gt;In this section you will create a non-root user with sudo privileges on all servers so that you can SSH into them manually as an unprivileged user. This can be useful if, for example, you would like to see system information with commands such as &lt;code&gt;top/htop&lt;/code&gt;, view a list of running containers, or change configuration files owned by root. These operations are routinely performed during the maintenance of a cluster, and using a non-root user for such tasks minimizes the risk of modifying or deleting important files or unintentionally performing other dangerous operations.&lt;/p&gt;

&lt;p&gt;Create a file named &lt;code&gt;~/kube-cluster/initial.yml&lt;/code&gt; in the workspace:&lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nano ~/kube-cluster/initial.yml
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, add the following &lt;em&gt;play&lt;/em&gt; to the file to create a non-root user with sudo privileges on all of the servers. A play in Ansible is a collection of steps to be performed that target specific servers and groups. The following play will create a non-root sudo user:&lt;/p&gt;
&lt;div class="code-label " title="~/kube-cluster/initial.yml"&gt;~/kube-cluster/initial.yml&lt;/div&gt;&lt;pre class="code-pre yaml local-environment"&gt;&lt;code langs=""&gt;- hosts: all
  become: yes
  tasks:
    - name: create the 'ubuntu' user
      user: name=ubuntu append=yes state=present createhome=yes shell=/bin/bash

    - name: allow 'ubuntu' to have passwordless sudo
      lineinfile:
        dest: /etc/sudoers
        line: 'ubuntu ALL=(ALL) NOPASSWD: ALL'
        validate: 'visudo -cf %s'

    - name: set up authorized keys for the ubuntu user
      authorized_key: user=ubuntu key="{{item}}"
      with_file:
        - ~/.ssh/id_rsa.pub
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here's a breakdown of what this playbook does:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Creates the non-root user &lt;code&gt;ubuntu&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Configures the &lt;code&gt;sudoers&lt;/code&gt; file to allow the &lt;code&gt;ubuntu&lt;/code&gt; user to run &lt;code&gt;sudo&lt;/code&gt; commands without a password prompt.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Adds the public key in your local machine (usually &lt;code&gt;~/.ssh/id_rsa.pub&lt;/code&gt;) to the remote &lt;code&gt;ubuntu&lt;/code&gt; user's authorized key list. This will allow you to SSH into each server as the &lt;code&gt;ubuntu&lt;/code&gt; user. &lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Save and close the file after you've added the text.&lt;/p&gt;

&lt;p&gt;Next, execute the playbook by locally running:&lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;ansible-playbook -i hosts ~/kube-cluster/initial.yml
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The command will complete within two to five minutes. On completion, you will see output similar to the following:&lt;/p&gt;
&lt;pre class="code-pre  local-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;PLAY [all] ****

TASK [Gathering Facts] ****
ok: [master]
ok: [worker1]
ok: [worker2]

TASK [create the 'ubuntu' user] ****
changed: [master]
changed: [worker1]
changed: [worker2]

TASK [allow 'ubuntu' user to have passwordless sudo] ****
changed: [master]
changed: [worker1]
changed: [worker2]

TASK [set up authorized keys for the ubuntu user] ****
changed: [worker1] =&amp;gt; (item=ssh-rsa AAAAB3...)
changed: [worker2] =&amp;gt; (item=ssh-rsa AAAAB3...)
changed: [master] =&amp;gt; (item=ssh-rsa AAAAB3...)

PLAY RECAP ****
master                     : ok=5    changed=4    unreachable=0    failed=0   
worker1                    : ok=5    changed=4    unreachable=0    failed=0   
worker2                    : ok=5    changed=4    unreachable=0    failed=0   
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that the preliminary setup is complete, you can move on to installing Kubernetes-specific dependencies.&lt;/p&gt;

&lt;h2 id="step-3-—-installing-kubernetetes-39-dependencies"&gt;Step 3 — Installing Kubernetetes' Dependencies&lt;/h2&gt;

&lt;p&gt;In this section, you will install the operating-system-level packages required by Kubernetes with Ubuntu's package manager. These packages are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Docker - a container runtime. It is the component that runs your containers. Support for other runtimes such as &lt;a href="https://coreos.com/rkt/"&gt;rkt&lt;/a&gt; is under active development in Kubernetes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;kubeadm&lt;/code&gt; - a CLI tool that will install and configure the various components of a cluster in a standard way.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;kubelet&lt;/code&gt; - a system service/program that runs on all nodes and handles node-level operations.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;kubectl&lt;/code&gt; - a CLI tool used for issuing commands to the cluster through its API Server. &lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Create a file named &lt;code&gt;~/kube-cluster/kube-dependencies.yml&lt;/code&gt; in the workspace:&lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nano ~/kube-cluster/kube-dependencies.yml
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add the following plays to the file to install these packages to your servers:&lt;/p&gt;
&lt;div class="code-label " title="~/kube-cluster/kube-dependencies.yml"&gt;~/kube-cluster/kube-dependencies.yml&lt;/div&gt;&lt;pre class="code-pre yaml local-environment"&gt;&lt;code langs=""&gt;- hosts: all
  become: yes
  tasks:
   - name: install Docker
     apt:
       name: docker.io
       state: present
       update_cache: true

   - name: install APT Transport HTTPS
     apt:
       name: apt-transport-https
       state: present

   - name: add Kubernetes apt-key
     apt_key:
       url: https://packages.cloud.google.com/apt/doc/apt-key.gpg
       state: present

   - name: add Kubernetes' APT repository
     apt_repository:
      repo: deb http://apt.kubernetes.io/ kubernetes-xenial main
      state: present
      filename: 'kubernetes'

   - name: install kubelet
     apt:
       name: kubelet
       state: present
       update_cache: true

   - name: install kubeadm
     apt:
       name: kubeadm
       state: present

- hosts: master
  become: yes
  tasks:
   - name: install kubectl
     apt:
       name: kubectl
       state: present
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first play in the playbook does the following: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Installs Docker, the container runtime.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Installs &lt;code&gt;apt-transport-https&lt;/code&gt;, allowing you to add external HTTPS sources to your APT sources list.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Adds the Kubernetes APT repository's apt-key for key verification.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Adds the Kubernetes APT repository to your remote servers' APT sources list.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Installs &lt;code&gt;kubelet&lt;/code&gt; and &lt;code&gt;kubeadm&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The second play consists of a single task that installs &lt;code&gt;kubectl&lt;/code&gt; on your master node.&lt;/p&gt;

&lt;p&gt;Save and close the file when you are finished.&lt;/p&gt;

&lt;p&gt;Next, execute the playbook by locally running:&lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;ansible-playbook -i hosts ~/kube-cluster/kube-dependencies.yml
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On completion, you will see output similar to the following:&lt;/p&gt;
&lt;pre class="code-pre  local-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;PLAY [all] ****

TASK [Gathering Facts] ****
ok: [worker1]
ok: [worker2]
ok: [master]

TASK [install Docker] ****
changed: [master]
changed: [worker1]
changed: [worker2]

TASK [install APT Transport HTTPS] *****
ok: [master]
ok: [worker1]
changed: [worker2]

TASK [add Kubernetes apt-key] *****
changed: [master]
changed: [worker1]
changed: [worker2]

TASK [add Kubernetes' APT repository] *****
changed: [master]
changed: [worker1]
changed: [worker2]

TASK [install kubelet] *****
changed: [master]
changed: [worker1]
changed: [worker2]

TASK [install kubeadm] *****
changed: [master]
changed: [worker1]
changed: [worker2]

PLAY [master] *****

TASK [Gathering Facts] *****
ok: [master]

TASK [install kubectl] ******
ok: [master]

PLAY RECAP ****
master                     : ok=9    changed=5    unreachable=0    failed=0   
worker1                    : ok=7    changed=5    unreachable=0    failed=0  
worker2                    : ok=7    changed=5    unreachable=0    failed=0  
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After execution, Docker, &lt;code&gt;kubeadm&lt;/code&gt;, and &lt;code&gt;kubelet&lt;/code&gt; will be installed on all of the remote servers. &lt;code&gt;kubectl&lt;/code&gt; is not a required component and is only needed for executing cluster commands. Installing it only on the master node makes sense in this context, since you will run &lt;code&gt;kubectl&lt;/code&gt; commands only from the master. Note, however, that &lt;code&gt;kubectl&lt;/code&gt; commands can be run from any of the worker nodes or from any machine where it can be installed and configured to point to a cluster. &lt;/p&gt;

&lt;p&gt;All system dependencies are now installed. Let's set up the master node and initialize the cluster.&lt;/p&gt;

&lt;h2 id="step-4-—-setting-up-the-master-node"&gt;Step 4 — Setting Up the Master Node&lt;/h2&gt;

&lt;p&gt;In this section, you will set up the master node. Before creating any playbooks, however, it's worth covering a few concepts such as &lt;em&gt;Pods&lt;/em&gt; and &lt;em&gt;Pod Network Plugins&lt;/em&gt;, since your cluster will include both. &lt;/p&gt;

&lt;p&gt;A pod is an atomic unit that runs one or more containers. These containers share resources such as file volumes and network interfaces in common. Pods are the basic unit of scheduling in Kubernetes: all containers in a pod are guaranteed to run on the same node that the pod is scheduled on.&lt;/p&gt;

&lt;p&gt;Each pod has its own IP address, and a pod on one node should be able to access a pod on another node using the pod's IP. Containers on a single node can communicate easily through a local interface. Communication between pods is more complicated, however, and requires a separate networking component that can transparently route traffic from a pod on one node to a pod on another.&lt;/p&gt;

&lt;p&gt;This functionality is provided by pod network plugins. For this cluster, you will use &lt;a href="https://github.com/coreos/flannel"&gt;Flannel&lt;/a&gt;, a stable and performant option.&lt;/p&gt;

&lt;p&gt;Create an Ansible playbook named &lt;code&gt;master.yml&lt;/code&gt; on your local machine:&lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nano ~/kube-cluster/master.yml
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add the following play to the file to initialize the cluster and install Flannel:&lt;/p&gt;
&lt;div class="code-label " title="~/kube-cluster/master.yml"&gt;~/kube-cluster/master.yml&lt;/div&gt;&lt;pre class="code-pre yaml local-environment"&gt;&lt;code langs=""&gt;- hosts: master
  become: yes
  tasks:
    - name: initialize the cluster
      shell: kubeadm init --pod-network-cidr=10.244.0.0/16 &amp;gt;&amp;gt; cluster_initialized.txt
      args:
        chdir: $HOME
        creates: cluster_initialized.txt

    - name: create .kube directory
      become: yes
      become_user: ubuntu
      file:
        path: $HOME/.kube
        state: directory
        mode: 0755

    - name: copy admin.conf to user's kube config
      copy:
        src: /etc/kubernetes/admin.conf
        dest: /home/ubuntu/.kube/config
        remote_src: yes
        owner: ubuntu

    - name: install Pod network
      become: yes
      become_user: ubuntu
      shell: kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/v0.9.1/Documentation/kube-flannel.yml &amp;gt;&amp;gt; pod_network_setup.txt
      args:
        chdir: $HOME
        creates: pod_network_setup.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here's a breakdown of this play:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The first task initializes the cluster by running &lt;code&gt;kubeadm init&lt;/code&gt;. Passing the argument &lt;code&gt;--pod-network-cidr=10.244.0.0/16&lt;/code&gt; specifies the private subnet that the pod IPs will be assigned from. Flannel uses the above subnet by default; we're telling &lt;code&gt;kubeadm&lt;/code&gt; to use the same subnet.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The second task creates a &lt;code&gt;.kube&lt;/code&gt; directory at &lt;code&gt;/home/ubuntu&lt;/code&gt;. This directory will hold configuration information such as the admin key files, which are required to connect to the cluster, and the cluster's API address.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The third task copies the &lt;code&gt;/etc/kubernetes/admin.conf&lt;/code&gt; file that was generated from &lt;code&gt;kubeadm init&lt;/code&gt; to your non-root user's home directory. This will allow you to use &lt;code&gt;kubectl&lt;/code&gt; to access the newly-created cluster.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The last task runs &lt;code&gt;kubectl apply&lt;/code&gt; to install &lt;code&gt;Flannel&lt;/code&gt;. &lt;code&gt;kubectl apply -f descriptor.[yml|json]&lt;/code&gt; is the syntax for telling &lt;code&gt;kubectl&lt;/code&gt; to create the objects described in the &lt;code&gt;descriptor.[yml|json]&lt;/code&gt; file. The &lt;code&gt;kube-flannel.yml&lt;/code&gt; file contains the descriptions of objects required for setting up &lt;code&gt;Flannel&lt;/code&gt; in the cluster.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Save and close the file when you are finished.&lt;/p&gt;

&lt;p&gt;Execute the playbook locally by running: &lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;ansible-playbook -i hosts ~/kube-cluster/master.yml
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On completion, you will see output similar to the following:&lt;/p&gt;
&lt;pre class="code-pre  local-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;
PLAY [master] ****

TASK [Gathering Facts] ****
ok: [master]

TASK [initialize the cluster] ****
changed: [master]

TASK [create .kube directory] ****
changed: [master]

TASK [copy admin.conf to user's kube config] *****
changed: [master]

TASK [install Pod network] *****
changed: [master]

PLAY RECAP ****
master                     : ok=5    changed=4    unreachable=0    failed=0  
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To check the status of the master node, SSH into it with the following command:&lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;ssh ubuntu@&lt;span class="highlight"&gt;master_ip&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once inside the master node, execute:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl get nodes
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will now see the following output:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;NAME      STATUS    ROLES     AGE       VERSION
master    Ready     master    1d        v1.11.1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The output states that the &lt;code&gt;master&lt;/code&gt; node has completed all initialization tasks and is in a &lt;code&gt;Ready&lt;/code&gt; state from which it can start accepting worker nodes and executing tasks sent to the API Server. You can now add the workers from your local machine.&lt;/p&gt;

&lt;h2 id="step-5-—-setting-up-the-worker-nodes"&gt;Step 5 — Setting Up the Worker Nodes&lt;/h2&gt;

&lt;p&gt;Adding workers to the cluster involves executing a single command on each. This command includes the necessary cluster information, such as the IP address and port of the master's API Server, and a secure token. Only nodes that pass in the secure token will be able join the cluster. &lt;/p&gt;

&lt;p&gt;Navigate back to your workspace and create a playbook named &lt;code&gt;workers.yml&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nano ~/kube-cluster/workers.yml
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add the following text to the file to add the workers to the cluster:&lt;/p&gt;
&lt;div class="code-label " title="~/kube-cluster/workers.yml"&gt;~/kube-cluster/workers.yml&lt;/div&gt;&lt;pre class="code-pre yaml local-environment"&gt;&lt;code langs=""&gt;- hosts: master
  become: yes
  gather_facts: false
  tasks:
    - name: get join command
      shell: kubeadm token create --print-join-command
      register: join_command_raw

    - name: set join command
      set_fact:
        join_command: "{{ join_command_raw.stdout_lines[0] }}"


- hosts: workers
  become: yes
  tasks:
    - name: join cluster
      shell: "{{ hostvars['master'].join_command }} &amp;gt;&amp;gt; node_joined.txt"
      args:
        chdir: $HOME
        creates: node_joined.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here's what the playbook does:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The first play gets the join command that needs to be run on the worker nodes. This command will be in the following format:&lt;code&gt;kubeadm join --token &amp;lt;token&amp;gt; &amp;lt;master-ip&amp;gt;:&amp;lt;master-port&amp;gt; --discovery-token-ca-cert-hash sha256:&amp;lt;hash&amp;gt;&lt;/code&gt;. Once it gets the actual command with the proper &lt;strong&gt;token&lt;/strong&gt; and &lt;strong&gt;hash&lt;/strong&gt; values, the task sets it as a fact so that the next play will be able to access that info.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The second play has a single task that runs the join command on all worker nodes. On completion of this task, the two worker nodes will be part of the cluster.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Save and close the file when you are finished.&lt;/p&gt;

&lt;p&gt;Execute the playbook by locally running:&lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;ansible-playbook -i hosts ~/kube-cluster/workers.yml
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On completion, you will see output similar to the following:&lt;/p&gt;
&lt;pre class="code-pre  local-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;PLAY [master] ****

TASK [get join command] ****
changed: [master]

TASK [set join command] *****
ok: [master]

PLAY [workers] *****

TASK [Gathering Facts] *****
ok: [worker1]
ok: [worker2]

TASK [join cluster] *****
changed: [worker1]
changed: [worker2]

PLAY RECAP *****
master                     : ok=2    changed=1    unreachable=0    failed=0   
worker1                    : ok=2    changed=1    unreachable=0    failed=0  
worker2                    : ok=2    changed=1    unreachable=0    failed=0  
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With the addition of the worker nodes, your cluster is now fully set up and functional, with workers ready to run workloads. Before scheduling applications, let's verify that the cluster is working as intended.&lt;/p&gt;

&lt;h2 id="step-6-—-verifying-the-cluster"&gt;Step 6 — Verifying the Cluster&lt;/h2&gt;

&lt;p&gt;A cluster can sometimes fail during setup because a node is down or network connectivity between the master and worker is not working correctly. Let's verify the cluster and ensure that the nodes are operating correctly.&lt;/p&gt;

&lt;p&gt;You will need to check the current state of the cluster from the master node to ensure that the nodes are ready. If you disconnected from the master node, you can SSH back into it with the following command: &lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;ssh ubuntu@&lt;span class="highlight"&gt;master_ip&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then execute the following command to get the status of the cluster: &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl get nodes
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will see output similar to the following:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;NAME      STATUS    ROLES     AGE       VERSION
master    Ready     master    1d        v1.11.1
worker1   Ready     &amp;lt;none&amp;gt;    1d        v1.11.1 
worker2   Ready     &amp;lt;none&amp;gt;    1d        v1.11.1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If all of your nodes have the value &lt;code&gt;Ready&lt;/code&gt; for &lt;code&gt;STATUS&lt;/code&gt;, it means that they're part of the cluster and ready to run workloads.&lt;/p&gt;

&lt;p&gt;If, however, a few of the nodes have &lt;code&gt;NotReady&lt;/code&gt; as the &lt;code&gt;STATUS&lt;/code&gt;, it could mean that the worker nodes haven't finished their setup yet. Wait for around five to ten minutes before re-running &lt;code&gt;kubectl get nodes&lt;/code&gt; and inspecting the new output. If a few nodes still have &lt;code&gt;NotReady&lt;/code&gt; as the status, you might have to verify and re-run the commands in the previous steps.&lt;/p&gt;

&lt;p&gt;Now that your cluster is verified successfully, let's schedule an example Nginx application on the cluster. &lt;/p&gt;

&lt;h2 id="step-7-—-running-an-application-on-the-cluster"&gt;Step 7 — Running An Application on the Cluster&lt;/h2&gt;

&lt;p&gt;You can now deploy any containerized application to your cluster. To keep things familiar, let's deploy Nginx using &lt;em&gt;Deployments&lt;/em&gt; and &lt;em&gt;Services&lt;/em&gt; to see how this application can be deployed to the cluster. You can use the commands below for other containerized applications as well, provided you change the Docker image name and any relevant flags (such as &lt;code&gt;ports&lt;/code&gt; and &lt;code&gt;volumes&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Still within the master node, execute the following command to create a deployment named &lt;code&gt;nginx&lt;/code&gt;: &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl run &lt;span class="highlight"&gt;nginx&lt;/span&gt; --image=&lt;span class="highlight"&gt;nginx&lt;/span&gt; --port &lt;span class="highlight"&gt;80&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A deployment is a type of Kubernetes object that ensures there's always a specified number of pods running based on a defined template, even if the pod crashes during the cluster's lifetime. The above deployment will create a pod with one container from the Docker registry's &lt;a href="https://hub.docker.com/_/nginx/"&gt;Nginx Docker Image&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Next, run the following command to create a service named &lt;code&gt;nginx&lt;/code&gt; that will expose the app publicly. It will do so through a &lt;em&gt;NodePort&lt;/em&gt;, a scheme that will make the pod accessible through an arbitrary port opened on each node of the cluster: &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl expose deploy &lt;span class="highlight"&gt;nginx&lt;/span&gt; --port &lt;span class="highlight"&gt;80&lt;/span&gt; --target-port &lt;span class="highlight"&gt;80&lt;/span&gt; --type NodePort
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Services are another type of Kubernetes object that expose cluster internal services to clients, both internal and external. They are also capable of load balancing requests to multiple pods, and are an integral component in Kubernetes, frequently interacting with other components. &lt;/p&gt;

&lt;p&gt;Run the following command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl get services
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will output text similar to the following:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;NAME         TYPE        CLUSTER-IP       EXTERNAL-IP           PORT(S)             AGE
kubernetes   ClusterIP   10.96.0.1        &amp;lt;none&amp;gt;                443/TCP             1d
&lt;span class="highlight"&gt;nginx&lt;/span&gt;        NodePort    10.109.228.209   &amp;lt;none&amp;gt;                80:&lt;span class="highlight"&gt;nginx_port&lt;/span&gt;/TCP   40m
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;From the third line of the above output, you can retrieve the port that Nginx is running on. Kubernetes will assign a random port that is greater than &lt;code&gt;30000&lt;/code&gt; automatically, while ensuring that the port is not already bound by another service.&lt;/p&gt;

&lt;p&gt;To test that everything is working, visit &lt;code&gt;http://&lt;span class="highlight"&gt;worker_1_ip&lt;/span&gt;:&lt;span class="highlight"&gt;nginx_port&lt;/span&gt;&lt;/code&gt; or &lt;code&gt;http://&lt;span class="highlight"&gt;worker_2_ip&lt;/span&gt;:&lt;span class="highlight"&gt;nginx_port&lt;/span&gt;&lt;/code&gt; through a browser on your local machine. You will see Nginx's familiar welcome page.&lt;/p&gt;

&lt;p&gt;If you would like to remove the Nginx application, first delete the &lt;code&gt;nginx&lt;/code&gt; service from the master node: &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl delete service &lt;span class="highlight"&gt;nginx&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Run the following to ensure that the service has been deleted:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl get services
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will see the following output:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;NAME         TYPE        CLUSTER-IP       EXTERNAL-IP           PORT(S)        AGE
kubernetes   ClusterIP   10.96.0.1        &amp;lt;none&amp;gt;                443/TCP        1d
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then delete the deployment:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl delete deployment &lt;span class="highlight"&gt;nginx&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Run the following to confirm that this worked:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl get deployments
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;No resources found.
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;In this guide, you've successfully set up a Kubernetes cluster on Ubuntu 18.04 using Kubeadm and Ansible for automation.&lt;/p&gt;

&lt;p&gt;If you're wondering what to do with the cluster now that it's set up, a good next step would be to get comfortable deploying your own applications and services onto the cluster. Here's a list of links with further information that can guide you in the process:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://docs.docker.com/engine/examples/"&gt;Dockerizing applications&lt;/a&gt; - lists examples that detail how to containerize applications using Docker.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://kubernetes.io/docs/concepts/workloads/pods/pod-overview/"&gt;Pod Overview&lt;/a&gt; - describes in detail how Pods work and their relationship with other Kubernetes objects. Pods are ubiquitous in Kubernetes, so understanding them will facilitate your work.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://kubernetes.io/docs/concepts/workloads/controllers/deployment/"&gt;Deployments Overview&lt;/a&gt; - provides an overview of deployments. It is useful to understand how controllers such as deployments work since they are used frequently in stateless applications for scaling and the automated healing of unhealthy applications.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://kubernetes.io/docs/concepts/services-networking/service/"&gt;Services Overview&lt;/a&gt; - covers services, another frequently used object in Kubernetes clusters. Understanding the types of services and the options they have is essential for running both stateless and stateful applications.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Other important concepts that you can look into are &lt;a href="https://kubernetes.io/docs/concepts/storage/volumes/"&gt;Volumes&lt;/a&gt;, &lt;a href="https://kubernetes.io/docs/concepts/services-networking/ingress/"&gt;Ingresses&lt;/a&gt; and &lt;a href="https://kubernetes.io/docs/concepts/configuration/secret/"&gt;Secrets&lt;/a&gt;, all of which come in handy when deploying production applications.&lt;/p&gt;

&lt;p&gt;Kubernetes has a lot of functionality and features to offer. &lt;a href="https://kubernetes.io/docs/"&gt;The Kubernetes Official Documentation&lt;/a&gt; is the best place to learn about concepts, find task-specific guides, and look up API references for various objects. &lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/building-optimized-containers-for-kubernetes</id>
    <published>2018-07-24T17:11:00Z</published>
    <updated>2018-09-19T19:14:48Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/building-optimized-containers-for-kubernetes"/>
    <title>Building Optimized Containers for Kubernetes</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;Container images are the primary packaging format for defining applications within Kubernetes.  Used as the basis for pods and other objects, images play an important role in leveraging Kubernetes' features to efficiently run applications on the platform.  Well-designed images are secure, highly performant, and focused.  They are able to react to configuration data or instructions provided by Kubernetes and also implement endpoints the orchestration system uses to understand internal application state.&lt;/p&gt;

&lt;p&gt;In this article, we'll introduce some strategies for creating high quality images and discuss a few general goals to help guide your decisions when containerizing applications.  We will focus on building images intended to be run on Kubernetes, but many of the suggestions apply equally to running containers on other orchestration platforms or in other contexts.&lt;/p&gt;

&lt;h2 id="characteristics-of-efficient-container-images"&gt;Characteristics of Efficient Container Images&lt;/h2&gt;

&lt;p&gt;Before we go over specific actions to take when building container images, we will talk about what makes a good container image.  What should your goals be when designing new images?  Which characteristics and what behavior are most important?&lt;/p&gt;

&lt;p&gt;Some qualities to aim for are:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A single, well-defined purpose&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Container images should have a single discrete focus.  Avoid thinking of container images as virtual machines, where it can make sense to package related functionality together.  Instead, treat your container images like Unix utilities, maintaining a strict focus on doing one small thing well.  Applications can be coordinated outside of the container scope to compose complex functionality.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Generic design with the ability to inject configuration at runtime&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Container images should be designed with reuse in mind when possible.  For instance, the ability to adjust configuration at runtime is often required to fulfill basic requirements like testing your images before deploying to production.  Small, generic images can be combined in different configurations to modify behavior without creating new images.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Small image size&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Smaller images have a number of benefits in clustered environments like Kubernetes.  They download quickly to new nodes and often have a smaller set of installed packages, which can improve security.  Pared down container images make it simpler to debug problems by minimizing the amount of software involved.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Externally managed state&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Containers in clustered environments experience a very volatile life cycle including planned and unplanned shutdowns due to resource scarcity, scaling, or node failures.  To maintain consistency, aid in recovery and availability of your services, and to avoid losing data, it is critical that you store application state in a stable location outside of the container.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Easy to understand&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It is important to try to keep container images as simple and easy to understand as possible.  When troubleshooting, being able to easily reason about the problem by viewing container image configuration or testing container behavior can help you reach a resolution faster.  Thinking of container images as a packaging format for your application instead of a machine configuration can help you strike the right balance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Follow containerized software best practices&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Images should aim to work within the container model instead of acting against it.  Avoid implementing conventional system administration practices, like including full init systems and daemonizing applications.  Log to standard out so Kubernetes can expose the data to administrators instead of using an internal logging daemon.  Each of these differs from best practices for full operating systems.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fully leverage Kubernetes features&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Beyond conforming to the container model, it's important to understand and reconcile with the environment and tooling that Kubernetes provides.  For example, providing endpoints for liveness and readiness checks or adjusting operation based on changes in the configuration or environment can help your applications use Kubernetes' dynamic deployment environment to their advantage.&lt;/p&gt;

&lt;p&gt;Now that we've established some of the qualities that define highly functional container images, we can dive deeper into strategies that help you achieve these goals.&lt;/p&gt;

&lt;h2 id="reuse-minimal-shared-base-layers"&gt;Reuse Minimal, Shared Base Layers&lt;/h2&gt;

&lt;p&gt;We can start off by examining the resources that container images are built from: base images.  Each container image is built either from a &lt;em&gt;parent image&lt;/em&gt;, an image used as a starting point, or from the abstract &lt;code&gt;scratch&lt;/code&gt; layer, an empty image layer with no filesystem.  A &lt;em&gt;base image&lt;/em&gt; is a container image that serves as a foundation for future images by defining the basic operating system and providing core functionality.  Images are comprised of one or more image layers built on top of one another to form a final image.&lt;/p&gt;

&lt;p&gt;No standard utilities or filesystem are available when working directly from &lt;code&gt;scratch&lt;/code&gt;, which means that you only have access to extremely limited functionality.  While images created directly from &lt;code&gt;scratch&lt;/code&gt; can be very streamlined and minimal, their main purpose is in defining base images.  Typically, you want to build your container images on top of a parent image that sets up a basic environment that your applications run in so that you do not have to construct a complete system for every image.&lt;/p&gt;

&lt;p&gt;While there are base images for a variety of Linux distributions, it's best to be deliberate about which systems you choose.  Each new machine will have to download the parent image and any additional layers you've added.  For large images, this can consume a significant amount of bandwidth and noticeably lengthen the startup time of your containers on their first run.  There is no way to pare down an image that's used as a parent downstream in the container build process, so starting with a minimal parent is a good idea.&lt;/p&gt;

&lt;p&gt;Feature rich environments like Ubuntu allow your application to run in an environment you're familiar with, but there are some tradeoffs to consider.  Ubuntu images (and similar conventional distribution images) tend to be relatively large (over 100MB), meaning that any container images built from them will inherit that weight.&lt;/p&gt;

&lt;p&gt;Alpine Linux is a popular alternative for base images because it successfully packages a lot of functionality into a very small base image (~ 5MB).  It includes a package manager with sizable repositories and has most of the standard utilities you would expect from a minimal Linux environment.&lt;/p&gt;

&lt;p&gt;When designing your applications, it's a good idea to try to reuse the same parent for each image.  When your images share a parent, machines running your containers will download the parent layer only once.  Afterwards, they will only need to download the layers that differ between your images.  This means that if you have common features or functionality you'd like to embed in each image, creating a common parent image to inherit from might be a good idea.  Images that share a lineage help minimize the amount of extra data you need to download on fresh servers.&lt;/p&gt;

&lt;h2 id="managing-container-layers"&gt;Managing Container Layers&lt;/h2&gt;

&lt;p&gt;Once you've selected a parent image, you can define your container image by adding additional software, copying files, exposing ports, and choosing processes to run.  Certain instructions in the image configuration file (a &lt;code&gt;Dockerfile&lt;/code&gt; if you are using Docker) will add additional layers to your image.&lt;/p&gt;

&lt;p&gt;For many of the same reasons mentioned in the previous section, it's important to be mindful of how you add layers to your images due to the resulting size, inheritance, and runtime complexity.  To avoid building large, unwieldy images, it's important to develop a good understanding of how container layers interact, how the build engine caches layers, and how subtle differences in similar instructions can have a big impact on the images you create.&lt;/p&gt;

&lt;h3 id="understanding-image-layers-and-build-cache"&gt;Understanding Image Layers and Build Cache&lt;/h3&gt;

&lt;p&gt;Docker creates a new image layer each time it executes a &lt;code&gt;RUN&lt;/code&gt;, &lt;code&gt;COPY&lt;/code&gt;, or &lt;code&gt;ADD&lt;/code&gt; instruction.  If you build the image again, the build engine will check each instruction to see if it has an image layer cached for the operation.  If it finds a match in the cache, it uses the existing image layer rather than executing the instruction again and rebuilding the layer.&lt;/p&gt;

&lt;p&gt;This process can significantly shorten build times, but it is important to understand the mechanism used to avoid potential problems.  For file copying instructions like &lt;code&gt;COPY&lt;/code&gt; and &lt;code&gt;ADD&lt;/code&gt;, Docker compares the checksums of the files to see if the operation needs to be performed again.  For &lt;code&gt;RUN&lt;/code&gt; instructions, Docker checks to see if it has an existing image layer cached for that particular command string.&lt;/p&gt;

&lt;p&gt;While it might not be immediately obvious, this behavior can cause unexpected results if you are not careful.  A common example of this is updating the local package index and installing packages in two separate steps.  We will be using Ubuntu for this example, but the basic premise applies equally well to base images for other distributions:&lt;/p&gt;
&lt;div class="code-label " title="Package installation example Dockerfile"&gt;Package installation example Dockerfile&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;FROM ubuntu:18.04
RUN apt -y update
RUN apt -y install nginx
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, the local package index is updated in one &lt;code&gt;RUN&lt;/code&gt; instruction (&lt;code&gt;apt -y update&lt;/code&gt;) and Nginx is installed in another operation.  This works without issue when it is first used.  However, if the Dockerfile is updated later to install an additional package, there may be problems:&lt;/p&gt;
&lt;div class="code-label " title="Package installation example Dockerfile"&gt;Package installation example Dockerfile&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;FROM ubuntu:18.04
RUN apt -y update
RUN apt -y install nginx &lt;span class="highlight"&gt;php-fpm&lt;/span&gt;
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We've added a second package to the installation command run by the second instruction.  If a significant amount of time has passed since the previous image build, the new build might fail.  That's because the package index update instruction (&lt;code&gt;RUN apt -y update&lt;/code&gt;) has &lt;em&gt;not&lt;/em&gt; changed, so Docker reuses the image layer associated with that instruction.  Since we are using an old package index, the version of the &lt;code&gt;php-fpm&lt;/code&gt; package we have in our local records may no longer be in the repositories, resulting in an error when the second instruction is run.&lt;/p&gt;

&lt;p&gt;To avoid this scenario, be sure to consolidate any steps that are interdependent into a single &lt;code&gt;RUN&lt;/code&gt; instruction so that Docker will re-execute all of the necessary commands when a change occurs:&lt;/p&gt;
&lt;div class="code-label " title="Package installation example Dockerfile"&gt;Package installation example Dockerfile&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;FROM ubuntu:18.04
RUN apt -y update &amp;amp;&amp;amp; apt -y install nginx &lt;span class="highlight"&gt;php-fpm&lt;/span&gt;
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The instruction now updates the local package cache whenever the package list changes.&lt;/p&gt;

&lt;h3 id="reducing-image-layer-size-by-tweaking-run-instructions"&gt;Reducing Image Layer Size by Tweaking RUN Instructions&lt;/h3&gt;

&lt;p&gt;The previous example demonstrates how Docker's caching behavior can subvert expectations, but there are some other things to keep in mind with how &lt;code&gt;RUN&lt;/code&gt; instructions interact with Docker's layering system.  As mentioned earlier, at the end of each &lt;code&gt;RUN&lt;/code&gt; instruction, Docker commits the changes as an additional image layer.  In order to exert control over the scope of the image layers produced, you can clean up unnecessary files in the final environment that will be committed by paying attention to the artifacts introduced by the commands you run.&lt;/p&gt;

&lt;p&gt;In general, chaining commands together into a single &lt;code&gt;RUN&lt;/code&gt; instruction offers a great deal of control over the layer that will be written.  For each command, you can set up the state of the layer (&lt;code&gt;apt -y update&lt;/code&gt;), perform the core command (&lt;code&gt;apt install -y nginx php-fpm&lt;/code&gt;), and remove any unnecessary artifacts to clean up the environment before it's committed.  For example, many Dockerfiles chain &lt;code&gt;rm -rf /var/lib/apt/lists/*&lt;/code&gt; to the end of &lt;code&gt;apt&lt;/code&gt; commands, removing the downloaded package indexes, to reduce the final layer size:&lt;/p&gt;
&lt;div class="code-label " title="Package installation example Dockerfile"&gt;Package installation example Dockerfile&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;FROM ubuntu:18.04
RUN apt -y update &amp;amp;&amp;amp; apt -y install nginx php-fpm &lt;span class="highlight"&gt;&amp;amp;&amp;amp; rm -rf /var/lib/apt/lists/*&lt;/span&gt;
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To further reduce the size of the image layers you are creating, trying to limit other unintended side effects of the commands you're running can be helpful.  For instance, in addition to the explicitly declared packages, &lt;code&gt;apt&lt;/code&gt; also installs "recommended" packages by default.  You can include &lt;code&gt;--no-install-recommends&lt;/code&gt; to your &lt;code&gt;apt&lt;/code&gt; commands to remove this behavior.  You may have to experiment to find out if you rely on any of the functionality provided by recommended packages.&lt;/p&gt;

&lt;p&gt;We've used package management commands in this section as an example, but these same principles apply to other scenarios.  The general idea is to construct the prerequisite conditions, execute the minimum viable command, and then clean up any unnecessary artifacts in a single &lt;code&gt;RUN&lt;/code&gt; command to reduce the overhead of the layer you'll be producing.&lt;/p&gt;

&lt;h3 id="using-multi-stage-builds"&gt;Using Multi-stage Builds&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://docs.docker.com/develop/develop-images/multistage-build/"&gt;&lt;strong&gt;Multi-stage builds&lt;/strong&gt;&lt;/a&gt; were introduced in Docker 17.05, allowing developers to more tightly control the final runtime images they produce.  Multi-stage builds allow you to divide your Dockerfile into multiple sections representing distinct stages, each with a &lt;code&gt;FROM&lt;/code&gt; statement to specify separate parent images.&lt;/p&gt;

&lt;p&gt;Earlier sections define images that can be used to build your application and prepare assets.  These often contain build tools and development files that are needed to produce the application, but are not necessary to run it.  Each subsequent stage defined in the file will have access to artifacts produced by previous stages.&lt;/p&gt;

&lt;p&gt;The last &lt;code&gt;FROM&lt;/code&gt; statement defines the image that will be used to run the application.  Typically, this is a pared down image that installs only the necessary runtime requirements and then copies the application artifacts produced by previous stages.&lt;/p&gt;

&lt;p&gt;This system allows you worry less about optimizing &lt;code&gt;RUN&lt;/code&gt; instructions in the build stages since those container layers will not be present in the final runtime image.  You should still pay attention to how instructions interact with layer caching in the build stages, but your efforts can be directed towards minimizing build time rather than final image size.  Paying attention to instructions in the final stage is still important in reducing image size, but by separating the different stages of your container build, it's easier to to obtain streamlined images without as much Dockerfile complexity.&lt;/p&gt;

&lt;h2 id="scoping-functionality-at-the-container-and-pod-level"&gt;Scoping Functionality at the Container and Pod Level&lt;/h2&gt;

&lt;p&gt;While the choices you make regarding container build instructions are important, broader decisions about how to containerize your services often have a more direct impact on your success.  In this section, we'll talk a bit more about how to best transition your applications from a more conventional environment to running on a container platform.&lt;/p&gt;

&lt;h3 id="containerizing-by-function"&gt;Containerizing by Function&lt;/h3&gt;

&lt;p&gt;Generally, it is good practice to package each piece of independent functionality into a separate container image.&lt;/p&gt;

&lt;p&gt;This differs from common strategies employed in virtual machine environments where applications are frequently grouped together within the same image to reduce the size and minimize the resources required to run the VM.  Since containers are lightweight abstractions that don't virtualize the entire operating system stack, this tradeoff is less compelling on Kubernetes.  So while a web stack virtual machine might bundle an Nginx web server with a Gunicorn application server on a single machine to serve a Django application, in Kubernetes these might be split into separate containers.&lt;/p&gt;

&lt;p&gt;Designing containers that implement one discrete piece of functionality for your services offers a number of advantages.  Each container can be developed independently if standard interfaces between services are established.  For instance, the Nginx container could potentially be used to proxy to a number of different backends or could be used as a load balancer if given a different configuration.&lt;/p&gt;

&lt;p&gt;Once deployed, each container image can be scaled independently to address varying resource and load constraints.  By splitting your applications into multiple container images, you gain flexibility in development, organization, and deployment.&lt;/p&gt;

&lt;h3 id="combining-container-images-in-pods"&gt;Combining Container Images in Pods&lt;/h3&gt;

&lt;p&gt;In Kubernetes, &lt;strong&gt;pods&lt;/strong&gt; are the smallest unit that can be directly managed by the control plane.  Pods consist of one or more containers along with additional configuration data to tell the platform how those components should be run.  The containers within a pod are always scheduled on the same worker node in the cluster and the system automatically restarts failed containers.  The pod abstraction is very useful, but it introduces another layer of decisions about how to bundle together the components of your applications.&lt;/p&gt;

&lt;p&gt;Like container images, pods also become less flexible when too much functionality is bundled into a single entity.  Pods themselves can be scaled using other abstractions, but the containers within cannot be managed or scaled independently.  So, to continue using our previous example, the separate Nginx and Gunicorn containers should probably not be bundled together into a single pod so that they can be controlled and deployed separately.&lt;/p&gt;

&lt;p&gt;However, there are scenarios where it does make sense to combine functionally different containers as a unit.  In general, these can be categorized as situations where an additional container supports or enhances the core functionality of the main container or helps it adapt to its deployment environment.  Some common patterns are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Sidecar&lt;/strong&gt;: The secondary container extends the main container's core functionality by acting in a supporting utility role.   For example, the sidecar container might forward logs or update the filesystem when a remote repository changes.  The primary container remains focused on its core responsibility, but is enhanced by the features provided by the sidecar.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ambassador&lt;/strong&gt;: An ambassador container is responsible for discovering and connecting to (often complex) external resources.  The primary container can connect to an ambassador container on well-known interfaces using the internal pod environment.  The ambassador abstracts the backend resources and proxies traffic between the primary container and the resource pool.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Adaptor&lt;/strong&gt;: An adaptor container is responsible for normalizing the primary containers interfaces, data, and protocols to align with the properties expected by other components.  The primary container can operate using native formats and the adaptor container translates and normalizes the data to communicate with the outside world.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As you might have noticed, each of these patterns support the strategy of building standard, generic primary container images that can then be deployed in a variety contexts and configurations.  The secondary containers help bridge the gap between the primary container and the specific deployment environment being used.  Some sidecar containers can also be reused to adapt multiple primary containers to the same environmental conditions.  These patterns benefit from the shared filesystem and networking namespace provided by the pod abstraction while still allowing independent development and flexible deployment of standardized containers.&lt;/p&gt;

&lt;h2 id="designing-for-runtime-configuration"&gt;Designing for Runtime Configuration&lt;/h2&gt;

&lt;p&gt;There is some tension between the desire to build standardized, reusable components and the requirements involved in adapting applications to their runtime environment.  Runtime configuration is one of the best methods to bridge the gap between these concerns.  Components are built to be both general and flexible and the required behavior is outlined at runtime by providing the software with additional configuration information.  This standard approach works for containers as well as it does for applications.&lt;/p&gt;

&lt;p&gt;Building with runtime configuration in mind requires you to think ahead during both the application development and containerization steps.  Applications should be designed to read values from command line parameters, configuration files, or environment variables when they are launched or restarted.  This configuration parsing and injection logic must be implemented in code prior to containerization.&lt;/p&gt;

&lt;p&gt;When writing a Dockerfile, the container must also be designed with runtime configuration in mind.  Containers have a number of mechanisms for providing data at runtime.  Users can mount files or directories from the host as volumes within the container to enable file-based configuration.  Likewise, environment variables can be passed into the internal container runtime when the container is started.  The &lt;code&gt;CMD&lt;/code&gt; and &lt;code&gt;ENTRYPOINT&lt;/code&gt; Dockerfile instructions can also be defined in a way that allows for runtime configuration information to be passed in as command parameters.&lt;/p&gt;

&lt;p&gt;Since Kubernetes manipulates higher level objects like pods instead of managing containers directly, there are mechanisms available to define configuration and inject it into the container environment at runtime.  Kubernetes &lt;strong&gt;ConfigMaps&lt;/strong&gt; and &lt;strong&gt;Secrets&lt;/strong&gt; allow you to define configuration data separately and then project the values into the container environment as environment variables or files at runtime.  ConfigMaps are general purpose objects intended to store configuration data that might vary based on environment, testing stage, etc.  Secrets offer a similar interface but are specifically designed for sensitive data, like account passwords or API credentials.&lt;/p&gt;

&lt;p&gt;By understanding and correctly using the runtime configuration options available throughout each layer of abstraction, you can build flexible components that take their cues from environment-provided values.  This makes it possible to reuse the same container images in very different scenarios, reducing development overhead by improving application flexibility.&lt;/p&gt;

&lt;h2 id="implementing-process-management-with-containers"&gt;Implementing Process Management with Containers&lt;/h2&gt;

&lt;p&gt;When transitioning to container-based environments, users often start by shifting existing workloads, with few or no changes, to the new system.  They package applications in containers by wrapping the tools they are already using in the new abstraction.  While it is helpful to use your usual patterns to get migrated applications up and running, dropping in previous implementations within containers can sometimes lead to ineffective design.&lt;/p&gt;

&lt;h3 id="treating-containers-like-applications-not-services"&gt;Treating Containers like Applications, Not Services&lt;/h3&gt;

&lt;p&gt;Problems frequently arise when developers implement significant service management functionality within containers.  For example, running systemd services within the container or daemonizing web servers may be considered best practices in a normal computing environment, but they often conflict with assumptions inherent in the container model.&lt;/p&gt;

&lt;p&gt;Hosts manage container life cycle events by sending signals to the process operating as PID (process ID) 1 inside the container.  PID 1 is the first process started, which would be the init system in traditional computing environments.  However, because the host can only manage PID 1, using a conventional init system to manage processes within the container sometimes means there is no way to control the primary application.  The host can start, stop, or kill the internal init system, but can't manage the primary application directly.  The signals sometimes propagate the intended behavior to the running application, but this adds complexity and isn't always necessary.&lt;/p&gt;

&lt;p&gt;Most of the time, it is better to simplify the running environment within the container so that PID 1 is running the primary application in the foreground.  In cases where multiple processes must be run, PID 1 is responsible for managing the life cycle of subsequent processes.  Certain applications, like Apache, handle this natively by spawning and managing workers that handle connections.  For other applications, a wrapper script or a very simple init system like &lt;a href="https://github.com/Yelp/dumb-init"&gt;dumb-init&lt;/a&gt; or the included &lt;a href="https://github.com/krallin/tini"&gt;tini&lt;/a&gt; init system can be used in some cases.  Regardless of the implementation you choose, the process running as PID 1 within the container should respond appropriately to &lt;code&gt;TERM&lt;/code&gt; signals sent by Kubernetes to behave as expected.&lt;/p&gt;

&lt;h3 id="managing-container-health-in-kubernetes"&gt;Managing Container Health in Kubernetes&lt;/h3&gt;

&lt;p&gt;Kubernetes deployments and services offer life cycle management for long-running processes and reliable, persistent access to applications, even when underlying containers need to be restarted or the implementations themselves change.  By extracting the responsibility of monitoring and maintaining service health out of the container, you can leverage the platform's tools for managing healthy workloads.&lt;/p&gt;

&lt;p&gt;In order for Kubernetes to manage containers properly, it has to understand whether the applications running within containers are healthy and capable of performing work.  To enable this, containers can implement liveness probes: network endpoints or commands that can be used to report application health.  Kubernetes will periodically check defined liveness probes to determine if the container is operating as expected.  If the container does not respond appropriately, Kubernetes restarts the container in an attempt to reestablish functionality.&lt;/p&gt;

&lt;p&gt;Kubernetes also provides readiness probes, a similar construct.  Rather than indicating whether the application within a container is healthy, readiness probes determine whether the application is ready to receive traffic.  This can be useful when a containerized application has an initialization routine that must complete before it is ready to receive connections.  Kubernetes uses readiness probes to determine whether to add a pod to or remove a pod from a service.&lt;/p&gt;

&lt;p&gt;Defining endpoints for these two probe types can help Kubernetes manage your containers efficiently and can prevent container life cycle problems from affecting service availability.  The mechanisms to respond to these types of health requests must be built into the application itself and must be exposed in the Docker image configuration.&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;In this guide, we've covered some important considerations to keep in mind when&lt;br&gt;
running containerized applications in Kubernetes.  To reiterate, some of the&lt;br&gt;
suggestions we went over were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use minimal, shareable parent images to build images with minimal bloat and reduce startup time&lt;/li&gt;
&lt;li&gt;Use multi-stage builds to separate the container build and runtime environments&lt;/li&gt;
&lt;li&gt;Combine Dockerfile instructions to create clean image layers and avoid image caching mistakes&lt;/li&gt;
&lt;li&gt;Containerize by isolating discrete functionality to enable flexible scaling and management&lt;/li&gt;
&lt;li&gt;Design pods to have a single, focused responsibility&lt;/li&gt;
&lt;li&gt;Bundle helper containers to enhance the main container's functionality or to adapt it to the deployment environment&lt;/li&gt;
&lt;li&gt;Build applications and containers to respond to runtime configuration to allow greater flexibility when deploying&lt;/li&gt;
&lt;li&gt;Run applications as the primary processes in containers so Kubernetes can manage life cycle events&lt;/li&gt;
&lt;li&gt;Develop health and liveness endpoints within the application or container so that Kubernetes can monitor the health of the container&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Throughout the development and implementation process, you will need to make decisions that can affect your service's robustness and effectiveness.  Understanding the ways that containerized applications differ from conventional applications, and learning how they operate in a managed cluster environment can help you avoid some common pitfalls and allow you to take advantage of all of the capabilities Kubernetes provides.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-install-apache-kafka-on-ubuntu-18-04</id>
    <published>2018-07-27T20:30:28Z</published>
    <updated>2018-08-03T19:13:13Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-install-apache-kafka-on-ubuntu-18-04"/>
    <title>How To Install Apache Kafka on Ubuntu 18.04</title>
    <content type="html">&lt;p&gt;&lt;em&gt;The author selected the &lt;a href="https://www.brightfunds.org/funds/foss-nonprofits"&gt;Free and Open Source Fund&lt;/a&gt; to receive a donation as part of the &lt;a href="https://do.co/w4do-cta"&gt;Write for DOnations&lt;/a&gt; program.&lt;/em&gt;&lt;/p&gt;

&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;&lt;a href="http://kafka.apache.org/"&gt;Apache Kafka&lt;/a&gt; is a popular distributed message broker designed to efficiently handle large volumes of real-time data. A Kafka cluster is not only highly scalable and fault-tolerant, but it also has a much higher throughput compared to other message brokers such as &lt;a href="http://activemq.apache.org/"&gt;ActiveMQ&lt;/a&gt; and &lt;a href="https://www.rabbitmq.com/"&gt;RabbitMQ&lt;/a&gt;. Though it is generally used as a &lt;em&gt;publish/subscribe&lt;/em&gt; messaging system, a lot of organizations also use it for log aggregation because it offers persistent storage for published messages.&lt;/p&gt;

&lt;p&gt;A publish/subscribe messaging system allows one or more producers to publish messages without considering the number of consumers or how they will process the messages. Subscribed clients are notified automatically about updates and the creation of new messages. This system is more efficient and scalable than systems where clients poll periodically to determine if new messages are available.&lt;/p&gt;

&lt;p&gt;In this tutorial, you will install and use Apache Kafka 1.1.0 on Ubuntu 18.04.&lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;To follow along, you will need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One Ubuntu 18.04 server and a non-root user with sudo privileges. Follow the steps specified in this &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-18-04"&gt;guide&lt;/a&gt; if you do not have a non-root user set up. &lt;/li&gt;
&lt;li&gt;At least 4GB of RAM on the server. Installations without this amount of RAM may cause the Kafka service to fail, with the &lt;a href="https://en.wikipedia.org/wiki/Java_virtual_machine"&gt;Java virtual machine (JVM)&lt;/a&gt; throwing an "Out Of Memory" exception during startup.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://openjdk.java.net/"&gt;OpenJDK&lt;/a&gt; 8 installed on your server. To install this version, follow &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-java-with-apt-on-ubuntu-18-04#installing-specific-versions-of-openjdk"&gt;these instructions&lt;/a&gt; on installing specific versions of OpenJDK. Kafka is written in Java, so it requires a JVM; however, its startup shell script has a version detection bug that causes it to fail to start with JVM versions above 8.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="step-1-—-creating-a-user-for-kafka"&gt;Step 1 — Creating a User for Kafka&lt;/h2&gt;

&lt;p&gt;Since Kafka can handle requests over a network, you should create a dedicated user for it. This minimizes damage to your Ubuntu machine should the Kafka server be compromised. We will create a dedicated &lt;strong&gt;kafka&lt;/strong&gt; user in this step, but you should create a different non-root user to perform other tasks on this server once you have finished setting up Kafka. &lt;/p&gt;

&lt;p&gt;Logged in as your non-root sudo user, create a user called &lt;strong&gt;kafka&lt;/strong&gt; with the &lt;code&gt;useradd&lt;/code&gt; command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo useradd kafka -m
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;-m&lt;/code&gt; flag ensures that a home directory will be created for the user. This home directory, &lt;code&gt;/home/kafka&lt;/code&gt;, will act as our workspace directory for executing commands in the sections below.&lt;/p&gt;

&lt;p&gt;Set the password using &lt;code&gt;passwd&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo passwd kafka
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add the &lt;strong&gt;kafka&lt;/strong&gt; user to the &lt;code&gt;sudo&lt;/code&gt; group with the &lt;code&gt;adduser&lt;/code&gt; command, so that it has the privileges required to install Kafka's dependencies:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo adduser kafka sudo
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Your &lt;strong&gt;kafka&lt;/strong&gt; user is now ready. Log into this account using &lt;code&gt;su&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;su -l kafka
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that we've created the Kafka-specific user, we can move on to downloading and extracting the Kafka binaries.&lt;/p&gt;

&lt;h2 id="step-2-—-downloading-and-extracting-the-kafka-binaries"&gt;Step 2 — Downloading and Extracting the Kafka Binaries&lt;/h2&gt;

&lt;p&gt;Let's download and extract the Kafka binaries into dedicated folders in our &lt;strong&gt;kafka&lt;/strong&gt; user's home directory.&lt;/p&gt;

&lt;p&gt;To start, create a directory in &lt;code&gt;/home/kafka&lt;/code&gt; called &lt;code&gt;Downloads&lt;/code&gt; to store your downloads:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;mkdir ~/Downloads
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Use &lt;code&gt;curl&lt;/code&gt; to download the Kafka binaries:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;curl "http://www-eu.apache.org/dist/kafka/&lt;span class="highlight"&gt;1.1.0&lt;/span&gt;/kafka_&lt;span class="highlight"&gt;2.12-1.1.0&lt;/span&gt;.tgz" -o ~/Downloads/kafka.tgz
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Create a directory called &lt;code&gt;kafka&lt;/code&gt; and change to this directory. This will be the base directory of the Kafka installation:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;mkdir ~/kafka &amp;amp;&amp;amp; cd ~/kafka
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Extract the archive you downloaded using the &lt;code&gt;tar&lt;/code&gt; command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;tar -xvzf ~/Downloads/kafka.tgz --strip 1
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We specify the &lt;code&gt;--strip 1&lt;/code&gt; flag to ensure that the archive's contents are extracted in &lt;code&gt;~/kafka/&lt;/code&gt; itself and not in another directory (such as &lt;code&gt;~/kafka/kafka_&lt;span class="highlight"&gt;2.12-1.1.0&lt;/span&gt;/&lt;/code&gt;) inside of it.&lt;/p&gt;

&lt;p&gt;Now that we've downloaded and extracted the binaries successfully, we can move on configuring to Kafka to allow for topic deletion. &lt;/p&gt;

&lt;h2 id="step-3-—-configuring-the-kafka-server"&gt;Step 3 — Configuring the Kafka Server&lt;/h2&gt;

&lt;p&gt;Kafka's default behavior will not allow us to delete a &lt;em&gt;topic&lt;/em&gt;, the category, group, or feed name to which messages can be published. To modify this, let's edit the configuration file. &lt;/p&gt;

&lt;p&gt;Kafka's configuration options are specified in &lt;code&gt;server.properties&lt;/code&gt;. Open this file with &lt;code&gt;nano&lt;/code&gt; or your favorite editor:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nano ~/kafka/config/server.properties
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let's add a setting that will allow us to delete Kafka topics.  Add the following to the bottom of the file:&lt;/p&gt;
&lt;div class="code-label " title="~/kafka/config/server.properties"&gt;~/kafka/config/server.properties&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;delete.topic.enable = true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save the file, and exit &lt;code&gt;nano&lt;/code&gt;. Now that we've configured Kafka, we can move on to creating systemd unit files for running and enabling it on startup. &lt;/p&gt;

&lt;h2 id="step-4-—-creating-systemd-unit-files-and-starting-the-kafka-server"&gt;Step 4 — Creating Systemd Unit Files and Starting the Kafka Server&lt;/h2&gt;

&lt;p&gt;In this section, we will create &lt;a href="https://www.digitalocean.com/community/tutorials/understanding-systemd-units-and-unit-files"&gt;systemd unit files&lt;/a&gt; for the Kafka service. This will help us perform common service actions such as starting, stopping, and restarting Kafka in a manner consistent with other Linux services.&lt;/p&gt;

&lt;p&gt;Zookeeper is a service that Kafka uses to manage its cluster state and configurations. It is commonly used in many distributed systems as an integral component. If you would like to know more about it, visit the official &lt;a href="https://zookeeper.apache.org/doc/current/index.html"&gt;Zookeeper docs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Create the unit file for &lt;code&gt;zookeeper&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/systemd/system/zookeeper.service
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Enter the following unit definition into the file:&lt;/p&gt;
&lt;div class="code-label " title="/etc/systemd/system/zookeeper.service"&gt;/etc/systemd/system/zookeeper.service&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;[Unit]
Requires=network.target remote-fs.target
After=network.target remote-fs.target

[Service]
Type=simple
User=kafka
ExecStart=/home/kafka/kafka/bin/zookeeper-server-start.sh /home/kafka/kafka/config/zookeeper.properties
ExecStop=/home/kafka/kafka/bin/zookeeper-server-stop.sh
Restart=on-abnormal

[Install]
WantedBy=multi-user.target
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;[Unit]&lt;/code&gt; section specifies that Zookeeper requires networking and the filesystem to be ready before it can start.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;[Service]&lt;/code&gt; section specifies that systemd should use the &lt;code&gt;zookeeper-server-start.sh&lt;/code&gt; and &lt;code&gt;zookeeper-server-stop.sh&lt;/code&gt; shell files for starting and stopping the service. It also specifies that Zookeeper should be restarted automatically if it exits abnormally.&lt;/p&gt;

&lt;p&gt;Next, create the systemd service file for &lt;code&gt;kafka&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/systemd/system/kafka.service
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Enter the following unit definition into the file:&lt;/p&gt;
&lt;div class="code-label " title="/etc/systemd/system/kafka.service"&gt;/etc/systemd/system/kafka.service&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;[Unit]
Requires=zookeeper.service
After=zookeeper.service

[Service]
Type=simple
User=kafka
ExecStart=/bin/sh -c '/home/kafka/kafka/bin/kafka-server-start.sh /home/kafka/kafka/config/server.properties &amp;gt; /home/kafka/kafka/kafka.log 2&amp;gt;&amp;amp;1'
ExecStop=/home/kafka/kafka/bin/kafka-server-stop.sh
Restart=on-abnormal

[Install]
WantedBy=multi-user.target
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;[Unit]&lt;/code&gt; section specifies that this unit file depends on &lt;code&gt;zookeeper.service&lt;/code&gt;. This will ensure that &lt;code&gt;zookeeper&lt;/code&gt; gets started automatically when the &lt;code&gt;kafa&lt;/code&gt; service starts.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;[Service]&lt;/code&gt; section specifies that systemd should use the &lt;code&gt;kafka-server-start.sh&lt;/code&gt; and &lt;code&gt;kafka-server-stop.sh&lt;/code&gt; shell files for starting and stopping the service. It also specifies that Kafka should be restarted automatically if it exits abnormally. &lt;/p&gt;

&lt;p&gt;Now that the units have been defined, start Kafka with the following command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl start kafka
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To ensure that the server has started successfully, check the journal logs for the &lt;code&gt;kafka&lt;/code&gt; unit:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;journalctl -u kafka
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see output similar to the following:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Jul 17 18:38:59 kafka-ubuntu systemd[1]: Started kafka.service.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You now have a Kafka server listening on port &lt;code&gt;9092&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;While we have started the &lt;code&gt;kafka&lt;/code&gt; service, if we were to reboot our server, it would not be started automatically. To enable &lt;code&gt;kafka&lt;/code&gt; on server boot, run:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl enable kafka
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that we've started and enabled the services, let's check the installation.&lt;/p&gt;

&lt;h2 id="step-5-—-testing-the-installation"&gt;Step 5 — Testing the Installation&lt;/h2&gt;

&lt;p&gt;Let's publish and consume a &lt;strong&gt;"Hello World"&lt;/strong&gt; message to make sure the Kafka server is behaving correctly. Publishing messages in Kafka requires:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;em&gt;producer&lt;/em&gt;, which enables the publication of records and data to topics. &lt;/li&gt;
&lt;li&gt;A &lt;em&gt;consumer&lt;/em&gt;, which reads messages and data from topics.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;First, create a topic named &lt;code&gt;TutorialTopic&lt;/code&gt; by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;~/kafka/bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic TutorialTopic
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can create a producer from the command line using the &lt;code&gt;kafka-console-producer.sh&lt;/code&gt; script. It expects the Kafka server's hostname, port, and a topic name as arguments.&lt;/p&gt;

&lt;p&gt;Publish the string &lt;code&gt;"Hello, World"&lt;/code&gt; to the &lt;code&gt;TutorialTopic&lt;/code&gt; topic by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;echo "Hello, World" | ~/kafka/bin/kafka-console-producer.sh --broker-list localhost:9092 --topic TutorialTopic &amp;gt; /dev/null
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, you can create a Kafka consumer using the &lt;code&gt;kafka-console-consumer.sh&lt;/code&gt; script. It expects the ZooKeeper server's hostname and port, along with a topic name as arguments. &lt;/p&gt;

&lt;p&gt;The following command consumes messages from &lt;code&gt;TutorialTopic&lt;/code&gt;. Note the use of the &lt;code&gt;--from-beginning&lt;/code&gt; flag, which allows the consumption of messages that were published before the consumer was started:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;~/kafka/bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic TutorialTopic --from-beginning
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If there are no configuration issues, you should see &lt;code&gt;Hello, World&lt;/code&gt; in your terminal:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Hello, World
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The script will continue to run, waiting for more messages to be published to the topic. Feel free to open a new terminal and start a producer to publish a few more messages. You should be able to see them all in the consumer's output.&lt;/p&gt;

&lt;p&gt;When you are done testing, press &lt;code&gt;CTRL+C&lt;/code&gt; to stop the consumer script. Now that we have tested the installation, let's move on to installing KafkaT. &lt;/p&gt;

&lt;h2 id="step-6-—-install-kafkat-optional"&gt;Step 6 — Install KafkaT (Optional)&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/airbnb/kafkat"&gt;KafkaT&lt;/a&gt; is a tool from Airbnb that makes it easier for you to view details about your Kafka cluster and perform certain administrative tasks from the command line. Because it is a Ruby gem, you will need Ruby to use it. You will also need the &lt;code&gt;build-essential&lt;/code&gt; package to be able to build the other gems it depends on. Install them using &lt;code&gt;apt&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install ruby ruby-dev build-essential
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can now install KafkaT using the gem command: &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo gem install kafkat
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;KafkaT uses &lt;code&gt;.kafkatcfg&lt;/code&gt; as the configuration file to determine the installation and log directories of your Kafka server. It should also have an entry pointing KafkaT to your ZooKeeper instance. &lt;/p&gt;

&lt;p&gt;Create a new file called &lt;code&gt;.kafkatcfg&lt;/code&gt;: &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nano ~/.kafkatcfg
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add the following lines to specify the required information about your Kafka server and Zookeeper instance: &lt;/p&gt;
&lt;div class="code-label " title="~/.kafkatcfg"&gt;~/.kafkatcfg&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;{
  "kafka_path": "~/kafka",
  "log_path": "/tmp/kafka-logs",
  "zk_path": "localhost:2181"
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You are now ready to use KafkaT. For a start, here's how you would use it to view details about all Kafka partitions:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kafkat partitions
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will see the following output:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Topic                 Partition   Leader      Replicas        ISRs    
TutorialTopic         0             0         [0]             [0]
__consumer_offsets    0               0           [0]                           [0]
...
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will see &lt;code&gt;TutorialTopic&lt;/code&gt;, as well as &lt;code&gt;__consumer_offsets&lt;/code&gt;, an internal topic used by Kafka for storing client-related information. You can safely ignore lines starting with &lt;code&gt;__consumer_offsets&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;To learn more about KafkaT, refer to its &lt;a href="https://github.com/airbnb/kafkat"&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id="step-7-—-setting-up-a-multi-node-cluster-optional"&gt;Step 7 — Setting Up a Multi-Node Cluster (Optional)&lt;/h2&gt;

&lt;p&gt;If you want to create a multi-broker cluster using more Ubuntu 18.04 machines, you should repeat Step 1, Step 4, and Step 5 on each of the new machines. Additionally, you should make the following changes in the &lt;code&gt;server.properties&lt;/code&gt; file for each:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The value of the &lt;code&gt;broker.id&lt;/code&gt; property should be changed such that it is unique throughout the cluster. This property uniquely identifies each server in the cluster and can have any string as its value. For example, &lt;code&gt;"server1"&lt;/code&gt;, &lt;code&gt;"server2"&lt;/code&gt;, etc. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The value of the &lt;code&gt;zookeeper.connect&lt;/code&gt; property should be changed such that all nodes point to the same ZooKeeper instance. This property specifies the Zookeeper instance's address and follows the &lt;code&gt;&amp;lt;HOSTNAME/IP_ADDRESS&amp;gt;:&amp;lt;PORT&amp;gt;&lt;/code&gt; format. For example, &lt;code&gt;"&lt;span class="highlight"&gt;203.0.113.0&lt;/span&gt;:2181"&lt;/code&gt;, &lt;code&gt;"&lt;span class="highlight"&gt;203.0.113.1&lt;/span&gt;:2181"&lt;/code&gt; etc. &lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want to have multiple ZooKeeper instances for your cluster, the value of the &lt;code&gt;zookeeper.connect&lt;/code&gt; property on each node should be an identical, comma-separated string listing the IP addresses and port numbers of all the ZooKeeper instances.&lt;/p&gt;

&lt;h2 id="step-8-—-restricting-the-kafka-user"&gt;Step 8 — Restricting the Kafka User&lt;/h2&gt;

&lt;p&gt;Now that all of the installations are done, you can remove the &lt;strong&gt;kafka&lt;/strong&gt; user's admin privileges. Before you do so, log out and log back in as any other non-root sudo user. If you are still running the same shell session you started this tutorial with, simply type &lt;code&gt;exit&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Remove the &lt;strong&gt;kafka&lt;/strong&gt; user from the sudo group:  &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo deluser kafka sudo
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To further improve your Kafka server's security, lock the &lt;strong&gt;kafka&lt;/strong&gt; user's password using the &lt;code&gt;passwd&lt;/code&gt; command. This makes sure that nobody can directly log into the server using this account: &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo passwd kafka -l
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At this point, only root or a sudo user can log in as &lt;code&gt;kafka&lt;/code&gt; by typing in the following command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo su - kafka
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the future, if you want to unlock it, use &lt;code&gt;passwd&lt;/code&gt; with the &lt;code&gt;-u&lt;/code&gt; option:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo passwd kafka -u
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You have now successfully restricted the &lt;strong&gt;kafka&lt;/strong&gt; user's admin privileges.&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;You now have Apache Kafka running securely on your Ubuntu server. You can make use of it in your projects by creating Kafka producers and consumers using &lt;a href="https://cwiki.apache.org/confluence/display/KAFKA/Clients"&gt;Kafka clients&lt;/a&gt;, which are available for most programming languages. To learn more about Kafka, you can also consult its &lt;a href="http://kafka.apache.org/documentation.html"&gt;documentation&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-create-a-multi-node-mysql-cluster-on-ubuntu-18-04</id>
    <published>2018-07-19T22:27:02Z</published>
    <updated>2018-08-03T19:20:06Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-create-a-multi-node-mysql-cluster-on-ubuntu-18-04"/>
    <title>How To Create a Multi-Node MySQL Cluster on Ubuntu 18.04</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;The MySQL Cluster distributed database provides high availability and throughput for your MySQL database management system. A MySQL Cluster consists of one or more management nodes (&lt;code&gt;ndb_mgmd&lt;/code&gt;) that store the cluster’s configuration and control the data nodes (&lt;code&gt;ndbd&lt;/code&gt;), where cluster data is stored. After communicating with the management node, clients (MySQL clients, servers, or native APIs) connect directly to these data nodes.&lt;/p&gt;

&lt;p&gt;With MySQL Cluster there is typically no replication of data, but instead data node synchronization. For this purpose a special data engine must be used — NDBCluster (NDB). It’s helpful to think of the cluster as a single logical MySQL environment with redundant components. Thus, a MySQL Cluster can participate in replication with other MySQL Clusters.&lt;/p&gt;

&lt;p&gt;MySQL Cluster works best in a shared-nothing environment. Ideally, no two components should share the same hardware. For simplicity and demonstration purposes, we'll limit ourselves to using only three servers. We will set up two servers as data nodes which sync data between themselves. The third server will be used for the Cluster Manager and also for the MySQL server/client. If you spin up additional servers, you can add more data nodes to the cluster, decouple the cluster manager from the MySQL server/client, and configure more servers as Cluster Managers and MySQL servers/clients.&lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;To complete this tutorial, you will need a total of three servers: two servers for the redundant MySQL data nodes (&lt;code&gt;ndbd&lt;/code&gt;), and one server for the Cluster Manager (&lt;code&gt;ndb_mgmd&lt;/code&gt;) and MySQL server/client (&lt;code&gt;mysqld&lt;/code&gt; and &lt;code&gt;mysql&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;In the &lt;strong&gt;same DigitalOcean data center&lt;/strong&gt;, create the following Droplets with &lt;strong&gt;private networking enabled&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Three Ubuntu 18.04 Droplets with &lt;a href="https://www.digitalocean.com/docs/networking/private-networking/quickstart/"&gt;private networking&lt;/a&gt; enabled&lt;/li&gt;
&lt;li&gt;A non-root user with sudo privileges configured for each Droplet. You can learn how to do this in &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-18-04"&gt;Initial Server Setup with Ubuntu 18.04&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Be sure to note down the &lt;strong&gt;private&lt;/strong&gt; IP addresses of your three Droplets. In this tutorial our cluster nodes have the following private IP addresses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&lt;span class="highlight"&gt;198.51.100.0&lt;/span&gt;&lt;/code&gt; will be the first MySQL Cluster data node&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&lt;span class="highlight"&gt;198.51.100.1&lt;/span&gt;&lt;/code&gt; will be the second data node&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&lt;span class="highlight"&gt;198.51.100.2&lt;/span&gt;&lt;/code&gt; will be the Cluster Manager &amp;amp; MySQL server node&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you’ve spun up your Droplets, configured a non-root user, and noted down the IP addresses for the 3 nodes, you’re ready to begin with this tutorial.&lt;/p&gt;

&lt;h2 id="step-1-—-installing-and-configuring-the-cluster-manager"&gt;Step 1 — Installing and Configuring the Cluster Manager&lt;/h2&gt;

&lt;p&gt;We’ll first begin by downloading and installing the MySQL Cluster Manager, &lt;code&gt;ndb_mgmd&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To install the Cluster Manager, we first need to fetch the appropriate &lt;code&gt;.deb&lt;/code&gt; installer file from the the official MySQL Cluster &lt;a href="http://dev.mysql.com/downloads/cluster/"&gt;download page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;From this page, under &lt;strong&gt;Select Operating System&lt;/strong&gt;, choose &lt;strong&gt;Ubuntu Linux&lt;/strong&gt;. Then, under &lt;strong&gt;Select OS Version&lt;/strong&gt;, choose &lt;strong&gt;Ubuntu Linux 18.04 (x86, 64-bit)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Scroll down until you see &lt;strong&gt;DEB Package, NDB Management Server&lt;/strong&gt;, and click on the &lt;strong&gt;Download&lt;/strong&gt; link for the one that does &lt;em&gt;not&lt;/em&gt; contain &lt;code&gt;dbgsym&lt;/code&gt; (unless you require debug symbols). You will be brought to a &lt;strong&gt;Begin Your Download&lt;/strong&gt; page. Here, right click on &lt;strong&gt;No thanks, just start my download.&lt;/strong&gt; and copy the link to the &lt;code&gt;.deb&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Now, log in to your Cluster Manager Droplet (in this tutorial, &lt;code&gt;&lt;span class="highlight"&gt;198.51.100.2&lt;/span&gt;&lt;/code&gt;), and download this &lt;code&gt;.deb&lt;/code&gt; file:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cd ~
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;wget https://dev.mysql.com/get/Downloads/MySQL-Cluster-7.6/mysql-cluster-community-management-server_&lt;span class="highlight"&gt;7.6.6-1&lt;/span&gt;ubuntu18.04_amd64.deb
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Install &lt;code&gt;ndb_mgmd&lt;/code&gt; using &lt;code&gt;dpkg&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo dpkg -i mysql-cluster-community-management-server_&lt;span class="highlight"&gt;7.6.6-1&lt;/span&gt;ubuntu18.04_amd64.deb
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We now need to configure &lt;code&gt;ndb_mgmd&lt;/code&gt; before first running it; proper configuration will ensure correct synchronization and load distribution among the data nodes.&lt;/p&gt;

&lt;p&gt;The Cluster Manager should be the first component launched in any MySQL cluster. It requires a configuration file, passed in as an argument to its executable. We’ll create and use the following configuration file: &lt;code&gt;/var/lib/mysql-cluster/config.ini&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;On the Cluster Manager Droplet, create the &lt;code&gt;/var/lib/mysql-cluster&lt;/code&gt; directory where this file will reside:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo mkdir /var/lib/mysql-cluster
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then create and edit the configuration file using your preferred text editor:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /var/lib/mysql-cluster/config.ini
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Paste the following text into your editor:&lt;/p&gt;
&lt;div class="code-label " title=" /var/lib/mysql-cluster/config.ini"&gt; /var/lib/mysql-cluster/config.ini&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;[ndbd default]
# Options affecting ndbd processes on all data nodes:
NoOfReplicas=2  # Number of replicas

[ndb_mgmd]
# Management process options:
hostname=&lt;span class="highlight"&gt;198.51.100.2&lt;/span&gt; # Hostname of the manager
datadir=/var/lib/mysql-cluster  # Directory for the log files

[ndbd]
hostname=&lt;span class="highlight"&gt;198.51.100.0&lt;/span&gt; # Hostname/IP of the first data node
NodeId=2            # Node ID for this data node
datadir=/usr/local/mysql/data   # Remote directory for the data files

[ndbd]
hostname=&lt;span class="highlight"&gt;198.51.100.1&lt;/span&gt; # Hostname/IP of the second data node
NodeId=3            # Node ID for this data node
datadir=/usr/local/mysql/data   # Remote directory for the data files

[mysqld]
# SQL node options:
hostname=&lt;span class="highlight"&gt;198.51.100.2&lt;/span&gt; # In our case the MySQL server/client is on the same Droplet as the cluster manager
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After pasting in this text, being sure to replace the &lt;code&gt;hostname&lt;/code&gt; values above with the correct IP addresses of the Droplets you’ve configured. Setting this &lt;code&gt;hostname&lt;/code&gt; parameter is an important security measure that prevents other servers from connecting to the Cluster Manager. &lt;/p&gt;

&lt;p&gt;Save the file and close your text editor.&lt;/p&gt;

&lt;p&gt;This is a pared-down, minimal configuration file for a MySQL Cluster. You should customize the parameters in this file depending on your production needs. For a sample, fully configured &lt;code&gt;ndb_mgmd&lt;/code&gt; configuration file, consult the MySQL Cluster &lt;a href="https://dev.mysql.com/doc/mysql-cluster-excerpt/5.7/en/mysql-cluster-config-starting.html"&gt;documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In the above file you can add additional components like data nodes (&lt;code&gt;ndbd&lt;/code&gt;) or MySQL server nodes (&lt;code&gt;mysqld&lt;/code&gt;) by appending instances to the appropriate section.&lt;/p&gt;

&lt;p&gt;We can now start the manager by executing the &lt;code&gt;ndb_mgmd&lt;/code&gt; binary and specifying its config file using the &lt;code&gt;-f&lt;/code&gt; flag:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ndb_mgmd -f /var/lib/mysql-cluster/config.ini
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see the following output:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;MySQL Cluster Management Server mysql-5.7.22 ndb-7.6.6
2018-07-25 21:48:39 [MgmtSrvr] INFO     -- The default config directory '/usr/mysql-cluster' does not exist. Trying to create it...
2018-07-25 21:48:39 [MgmtSrvr] INFO     -- Successfully created config directory
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This indicates that the MySQL Cluster Management server has successfully been installed and is now running on your Droplet.&lt;/p&gt;

&lt;p&gt;Ideally, we’d like to start the Cluster Management server automatically on boot. To do this, we’re going to create and enable a systemd service. &lt;/p&gt;

&lt;p&gt;Before we create the service, we need to kill the running server:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo pkill -f ndb_mgmd
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, open and edit the following systemd Unit file using your favorite editor:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/systemd/system/ndb_mgmd.service
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Paste in the following code:&lt;/p&gt;
&lt;div class="code-label " title="/etc/systemd/system/ndb_mgmd.service"&gt;/etc/systemd/system/ndb_mgmd.service&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;[Unit]
Description=MySQL NDB Cluster Management Server
After=network.target auditd.service

[Service]
Type=forking
ExecStart=/usr/sbin/ndb_mgmd -f /var/lib/mysql-cluster/config.ini
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
Restart=on-failure

[Install]
WantedBy=multi-user.target
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, we’ve added a minimal set of options instructing systemd on how to start, stop and restart the &lt;code&gt;ndb_mgmd&lt;/code&gt; process. To learn more about the options used in this unit configuration, consult the systemd &lt;a href="https://www.freedesktop.org/software/systemd/man/systemd.service.html"&gt;manual&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Save and close the file.&lt;/p&gt;

&lt;p&gt;Now, reload systemd’s manager configuration using &lt;code&gt;daemon-reload&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl daemon-reload
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We’ll enable the service we just created so that the MySQL Cluster Manager starts on reboot:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl enable ndb_mgmd
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, we’ll start the service:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl start ndb_mgmd
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can verify that the NDB Cluster Management service is running:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl status ndb_mgmd
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see the following output:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;● ndb_mgmd.service - MySQL NDB Cluster Management Server
   Loaded: loaded (/etc/systemd/system/ndb_mgmd.service; enabled; vendor preset: enabled)
   Active: active (running) since Thu 2018-07-26 21:23:37 UTC; 3s ago
  Process: 11184 ExecStart=/usr/sbin/ndb_mgmd -f /var/lib/mysql-cluster/config.ini (code=exited, status=0/SUCCESS)
 Main PID: 11193 (ndb_mgmd)
    Tasks: 11 (limit: 4915)
   CGroup: /system.slice/ndb_mgmd.service
           └─11193 /usr/sbin/ndb_mgmd -f /var/lib/mysql-cluster/config.ini
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Which indicates that the &lt;code&gt;ndb_mgmd&lt;/code&gt; MySQL Cluster Management server is now running as a systemd service. &lt;/p&gt;

&lt;p&gt;The final step for setting up the Cluster Manager is to allow incoming connections from other MySQL Cluster nodes on our private network.&lt;/p&gt;

&lt;p&gt;If you did not configure the &lt;code&gt;ufw&lt;/code&gt; firewall when setting up this Droplet, you can skip ahead to the next section.&lt;/p&gt;

&lt;p&gt;We’ll add rules to allow local incoming connections from both data nodes:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw allow from &lt;span class="highlight"&gt;198.51.100.0&lt;/span&gt;
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo ufw allow from &lt;span class="highlight"&gt;198.51.100.1&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After entering these commands, you should see the following output:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Rule added
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The Cluster Manager should now be up and running, and able to communicate with other Cluster nodes over the private network.&lt;/p&gt;

&lt;h2 id="step-2-—-installing-and-configuring-the-data-nodes"&gt;Step 2 — Installing and Configuring the Data Nodes&lt;/h2&gt;

&lt;p&gt;&lt;span class='note'&gt;&lt;strong&gt;Note:&lt;/strong&gt; All the commands in this section should be executed on both data nodes.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;In this step, we'll install the &lt;code&gt;ndbd&lt;/code&gt; MySQL Cluster data node daemon, and configure the nodes so they can communicate with the Cluster Manager.&lt;/p&gt;

&lt;p&gt;To install the data node binaries we first need to fetch the appropriate &lt;code&gt;.deb&lt;/code&gt; installer file from the official MySQL &lt;a href="http://dev.mysql.com/downloads/cluster/"&gt;download page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;From this page, under &lt;strong&gt;Select Operating System&lt;/strong&gt;, choose &lt;strong&gt;Ubuntu Linux&lt;/strong&gt;. Then, under &lt;strong&gt;Select OS Version&lt;/strong&gt;, choose &lt;strong&gt;Ubuntu Linux 18.04 (x86, 64-bit)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Scroll down until you see &lt;strong&gt;DEB Package, NDB Data Node Binaries&lt;/strong&gt;, and click on the &lt;strong&gt;Download&lt;/strong&gt; link for the one that does &lt;strong&gt;not&lt;/strong&gt; contain &lt;code&gt;dbgsym&lt;/code&gt; (unless you require debug symbols). You will be brought to a &lt;strong&gt;Begin Your Download&lt;/strong&gt; page. Here, right click on &lt;strong&gt;No thanks, just start my download.&lt;/strong&gt; and copy the link to the &lt;code&gt;.deb&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Now, log in to your first data node Droplet (in this tutorial, &lt;code&gt;&lt;span class="highlight"&gt;198.51.100.0&lt;/span&gt;&lt;/code&gt;), and download this &lt;code&gt;.deb&lt;/code&gt; file:&lt;/p&gt;
&lt;pre class="code-pre command second-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cd ~
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;wget https://dev.mysql.com/get/Downloads/MySQL-Cluster-7.6/mysql-cluster-community-data-node_&lt;span class="highlight"&gt;7.6.6-1&lt;/span&gt;ubuntu18.04_amd64.deb
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Before we install the data node binary, we need to install a dependency, &lt;code&gt;libclass-methodmaker-perl&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command second-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt update
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo apt install libclass-methodmaker-perl
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can now install the data note binary using &lt;code&gt;dpkg&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command second-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo dpkg -i mysql-cluster-community-data-node_&lt;span class="highlight"&gt;7.6.6-1&lt;/span&gt;ubuntu18.04_amd64.deb
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The data nodes pull their configuration from MySQL’s standard location,  &lt;code&gt;/etc/my.cnf&lt;/code&gt;. Create this file using your favorite text editor and begin editing it:&lt;/p&gt;
&lt;pre class="code-pre command second-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/my.cnf
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add the following configuration parameter to the file:&lt;/p&gt;
&lt;div class="code-label " title=" /etc/my.cnf"&gt; /etc/my.cnf&lt;/div&gt;&lt;pre class="code-pre  second-environment"&gt;&lt;code langs=""&gt;[mysql_cluster]
# Options for NDB Cluster processes:
ndb-connectstring=&lt;span class="highlight"&gt;198.51.100.2&lt;/span&gt;  # location of cluster manager
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Specifying the location of the Cluster Manager node is the only configuration needed for &lt;code&gt;ndbd&lt;/code&gt; to start. The rest of the configuration will be pulled from the manager directly.&lt;/p&gt;

&lt;p&gt;Save and exit the file.&lt;/p&gt;

&lt;p&gt;In our example, the data node will find out that its data directory is &lt;code&gt;/usr/local/mysql/data&lt;/code&gt;, per the manager's configuration. Before starting the daemon, we’ll create this directory on the node:&lt;/p&gt;
&lt;pre class="code-pre command second-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo mkdir -p /usr/local/mysql/data
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we can start the data node using the following command:&lt;/p&gt;
&lt;pre class="code-pre command second-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ndbd
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see the following output:&lt;/p&gt;
&lt;pre class="code-pre  second-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;2018-07-18 19:48:21 [ndbd] INFO     -- Angel connected to '&lt;span class="highlight"&gt;198.51.100.2&lt;/span&gt;:1186'
2018-07-18 19:48:21 [ndbd] INFO     -- Angel allocated nodeid: 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The NDB data node daemon has been successfully installed and is now running on your server.&lt;/p&gt;

&lt;p&gt;We also need to allow incoming connections from other MySQL Cluster nodes over the private network.&lt;/p&gt;

&lt;p&gt;If you did not configure the &lt;code&gt;ufw&lt;/code&gt; firewall when setting up this Droplet, you can skip ahead to setting up the systemd service for &lt;code&gt;ndbd&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We’ll add rules to allow incoming connections from the Cluster Manager and other data nodes:&lt;/p&gt;
&lt;pre class="code-pre command second-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw allow from &lt;span class="highlight"&gt;198.51.100.0&lt;/span&gt;
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo ufw allow from &lt;span class="highlight"&gt;198.51.100.2&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After entering these commands, you should see the following output:&lt;/p&gt;
&lt;pre class="code-pre  second-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Rule added
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Your MySQL data node Droplet can now communicate with both the Cluster Manager and other data node over the private network. &lt;/p&gt;

&lt;p&gt;Finally, we’d also like the data node daemon to start up automatically when the server boots. We’ll follow the same procedure used for the Cluster Manager, and create a systemd service.&lt;/p&gt;

&lt;p&gt;Before we create the service, we’ll kill the running &lt;code&gt;ndbd&lt;/code&gt; process:&lt;/p&gt;
&lt;pre class="code-pre command second-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo pkill -f ndbd
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, open and edit the following systemd Unit file using your favorite editor:&lt;/p&gt;
&lt;pre class="code-pre command second-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/systemd/system/ndbd.service
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Paste in the following code:&lt;/p&gt;
&lt;div class="code-label " title="/etc/systemd/system/ndbd.service"&gt;/etc/systemd/system/ndbd.service&lt;/div&gt;&lt;pre class="code-pre  second-environment"&gt;&lt;code langs=""&gt;[Unit]
Description=MySQL NDB Data Node Daemon
After=network.target auditd.service

[Service]
Type=forking
ExecStart=/usr/sbin/ndbd
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
Restart=on-failure

[Install]
WantedBy=multi-user.target
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, we’ve added a minimal set of options instructing systemd on how to start, stop and restart the &lt;code&gt;ndbd&lt;/code&gt; process. To learn more about the options used in this unit configuration, consult the systemd &lt;a href="https://www.freedesktop.org/software/systemd/man/systemd.service.html"&gt;manual&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Save and close the file.&lt;/p&gt;

&lt;p&gt;Now, reload systemd’s manager configuration using &lt;code&gt;daemon-reload&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command second-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl daemon-reload
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We’ll now enable the service we just created so that the data node daemon starts on reboot:&lt;/p&gt;
&lt;pre class="code-pre command second-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl enable ndbd
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, we’ll start the service:&lt;/p&gt;
&lt;pre class="code-pre command second-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl start ndbd
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can verify that the NDB Cluster Management service is running:&lt;/p&gt;
&lt;pre class="code-pre command second-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl status ndbd
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see the following output:&lt;/p&gt;
&lt;pre class="code-pre  second-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;● ndbd.service - MySQL NDB Data Node Daemon
   Loaded: loaded (/etc/systemd/system/ndbd.service; enabled; vendor preset: enabled)
   Active: active (running) since Thu 2018-07-26 20:56:29 UTC; 8s ago
  Process: 11972 ExecStart=/usr/sbin/ndbd (code=exited, status=0/SUCCESS)
 Main PID: 11984 (ndbd)
    Tasks: 46 (limit: 4915)
   CGroup: /system.slice/ndbd.service
           ├─11984 /usr/sbin/ndbd
           └─11987 /usr/sbin/ndbd
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Which indicates that the &lt;code&gt;ndbd&lt;/code&gt; MySQL Cluster data node daemon is now running as a systemd service. Your data node should now be fully functional and able to connect to the MySQL Cluster Manager.&lt;/p&gt;

&lt;p&gt;Once you’ve finished setting up the first data node, repeat the steps in this section on the other data node (&lt;code&gt;&lt;span class="highlight"&gt;198.51.100.1&lt;/span&gt;&lt;/code&gt; in this tutorial).&lt;/p&gt;

&lt;h2 id="step-3-—-configuring-and-starting-the-mysql-server-and-client"&gt;Step 3 — Configuring and Starting the MySQL Server and Client&lt;/h2&gt;

&lt;p&gt;A standard MySQL server, such as the one available in Ubuntu's APT repository, does not support the MySQL Cluster engine NDB. This means we need to install the custom SQL server packaged with the other MySQL Cluster software we’ve installed in this tutorial.&lt;/p&gt;

&lt;p&gt;We’ll once again grab the MySQL Cluster Server binary from the official MySQL Cluster &lt;a href="http://dev.mysql.com/downloads/cluster/"&gt;download page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;From this page, under &lt;strong&gt;Select Operating System&lt;/strong&gt;, choose &lt;strong&gt;Ubuntu Linux&lt;/strong&gt;. Then, under &lt;strong&gt;Select OS Version&lt;/strong&gt;, choose &lt;strong&gt;Ubuntu Linux 18.04 (x86, 64-bit)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Scroll down until you see &lt;strong&gt;DEB Bundle&lt;/strong&gt;, and click on the &lt;strong&gt;Download&lt;/strong&gt; link (it should be the first one in the list). You will be brought to a &lt;strong&gt;Begin Your Download&lt;/strong&gt; page. Here, right click on &lt;strong&gt;No thanks, just start my download.&lt;/strong&gt; and copy the link to the &lt;code&gt;.tar&lt;/code&gt; archive.&lt;/p&gt;

&lt;p&gt;Now, log in to the Cluster Manager Droplet (in this tutorial, &lt;code&gt;&lt;span class="highlight"&gt;198.51.100.2&lt;/span&gt;&lt;/code&gt;), and download this &lt;code&gt;.tar&lt;/code&gt; archive (recall that we are installing MySQL Server on the same node as our Cluster Manager – in a production setting you should run these daemons on different nodes):&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cd ~
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;wget https://dev.mysql.com/get/Downloads/MySQL-Cluster-7.6/mysql-cluster_&lt;span class="highlight"&gt;7.6.6-1&lt;/span&gt;ubuntu18.04_amd64.deb-bundle.tar
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We’ll now extract this archive into a directory called &lt;code&gt;install&lt;/code&gt;. First, create the directory:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;mkdir install
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now extract the archive into this directory:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;tar -xvf mysql-cluster_&lt;span class="highlight"&gt;7.6.6-1&lt;/span&gt;ubuntu18.04_amd64.deb-bundle.tar -C install/
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Move into this directory, containing the extracted MySQL Cluster component binaries:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cd install
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Before we install the MySQL server binary, we need to install a couple of dependencies:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt update
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo apt install libaio1 libmecab2
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, we need to install the MySQL Cluster dependencies, bundled in the &lt;code&gt;tar&lt;/code&gt; archive we just extracted :&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo dpkg -i mysql-common_&lt;span class="highlight"&gt;7.6.6-1&lt;/span&gt;ubuntu18.04_amd64.deb
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo dpkg -i mysql-cluster-community-client_&lt;span class="highlight"&gt;7.6.6-1&lt;/span&gt;ubuntu18.04_amd64.deb
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo dpkg -i mysql-client_&lt;span class="highlight"&gt;7.6.6-1&lt;/span&gt;ubuntu18.04_amd64.deb
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo dpkg -i mysql-cluster-community-server_&lt;span class="highlight"&gt;7.6.6-1&lt;/span&gt;ubuntu18.04_amd64.deb
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When installing &lt;code&gt;mysql-cluster-community-server&lt;/code&gt;, a configuration prompt should appear, asking you to set a password for the &lt;strong&gt;root&lt;/strong&gt; account of your MySQL database. Choose a strong, secure password, and hit &lt;strong&gt;&amp;lt;Ok&amp;gt;&lt;/strong&gt;. Re-enter this &lt;strong&gt;root&lt;/strong&gt; password when prompted, and hit &lt;strong&gt;&amp;lt;Ok&amp;gt;&lt;/strong&gt; once again to complete installation.&lt;/p&gt;

&lt;p&gt;We can now install the MySQL server binary using &lt;code&gt;dpkg&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;mysql-server_&lt;span class="highlight"&gt;7.6.6-1&lt;/span&gt;ubuntu18.04_amd64.deb
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We now need to configure this MySQL server installation.&lt;/p&gt;

&lt;p&gt;The configuration for MySQL Server is stored in the default &lt;code&gt;/etc/mysql/my.cnf&lt;/code&gt; file. &lt;/p&gt;

&lt;p&gt;Open this configuration file using your favorite editor:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/mysql/my.cnf
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see the following text:&lt;/p&gt;
&lt;div class="code-label " title="/etc/mysql/my.cnf"&gt;/etc/mysql/my.cnf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;# Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; version 2 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA

#
# The MySQL Cluster Community Server configuration file.
#
# For explanations see
# http://dev.mysql.com/doc/mysql/en/server-system-variables.html

# * IMPORTANT: Additional settings that can override those from this file!
#   The files must end with '.cnf', otherwise they'll be ignored.
#
!includedir /etc/mysql/conf.d/
!includedir /etc/mysql/mysql.conf.d/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Append the following configuration to it:&lt;/p&gt;
&lt;div class="code-label " title="/etc/mysql/my.cnf"&gt;/etc/mysql/my.cnf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .
[mysqld]
# Options for mysqld process:
ndbcluster                      # run NDB storage engine

[mysql_cluster]
# Options for NDB Cluster processes:
ndb-connectstring=&lt;span class="highlight"&gt;198.51.100.2&lt;/span&gt;  # location of management server
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save and exit the file.&lt;/p&gt;

&lt;p&gt;Restart the MySQL server for these changes to take effect:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl restart mysql
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;MySQL by default should start automatically when your server reboots. If it doesn’t, the following command should fix this:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl enable mysql
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A SQL server should now be running on your Cluster Manager / MySQL Server Droplet.&lt;/p&gt;

&lt;p&gt;In the next step, we’ll run a few commands to verify that our MySQL Cluster installation is functioning as expected.&lt;/p&gt;

&lt;h2 id="step-4-—-verifying-mysql-cluster-installation"&gt;Step 4 — Verifying MySQL Cluster Installation&lt;/h2&gt;

&lt;p&gt;To verify your MySQL Cluster installation, log in to your Cluster Manager / SQL Server node.&lt;/p&gt;

&lt;p&gt;We’ll open the MySQL client from the command line and connect to the &lt;strong&gt;root&lt;/strong&gt; account we just configured by entering the following command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;mysql -u root -p 
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Enter your password when prompted, and hit &lt;code&gt;ENTER&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You should see an output similar to:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 3
Server version: 5.7.22-ndb-7.6.6 MySQL Cluster Community Server (GPL)

Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once inside the MySQL client, run the following command:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;SHOW ENGINE NDB STATUS \G
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should now see information about the NDB cluster engine, beginning with connection parameters:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;
*************************** 1. row ***************************
  Type: ndbcluster
  Name: connection
Status: cluster_node_id=4, connected_host=&lt;span class="highlight"&gt;198.51.100.2&lt;/span&gt;, connected_port=1186, number_of_data_nodes=2, number_of_ready_data_nodes=2, connect_count=0
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This indicates that you’ve successfully connected to your MySQL Cluster.&lt;/p&gt;

&lt;p&gt;Notice here the number of &lt;code&gt;ready_data_nodes&lt;/code&gt;: 2. This redundancy allows your MySQL cluster to continue operating even if one of the data nodes fails. It also means that your SQL queries will be load balanced across the two data nodes.&lt;/p&gt;

&lt;p&gt;You can try shutting down one of the data nodes to test cluster stability. The simplest test would be to restart the data node Droplet in order to fully test the recovery process. You should see the value of &lt;code&gt;number_of_ready_data_nodes&lt;/code&gt; change to &lt;code&gt;1&lt;/code&gt; and back up to &lt;code&gt;2&lt;/code&gt; again as the node reboots and reconnects to the Cluster Manager.&lt;/p&gt;

&lt;p&gt;To exit the MySQL prompt, simply type &lt;code&gt;quit&lt;/code&gt; or press &lt;code&gt;CTRL-D&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is the first test that indicates that the MySQL cluster, server, and client are working. We'll now go through an additional test to confirm that the cluster is functioning properly.&lt;/p&gt;

&lt;p&gt;Open the Cluster management console, &lt;code&gt;ndb_mgm&lt;/code&gt; using the command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;ndb_mgm
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see the following output:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;-- NDB Cluster -- Management Client --
ndb_mgm&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once inside the console enter the command &lt;code&gt;SHOW&lt;/code&gt; and hit &lt;code&gt;ENTER&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="ndb_mgm&amp;gt;"&gt;SHOW
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see the following output:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Connected to Management Server at: &lt;span class="highlight"&gt;198.51.100.2&lt;/span&gt;:1186
Cluster Configuration
---------------------
[ndbd(NDB)] 2 node(s)
id=2    @&lt;span class="highlight"&gt;198.51.100.0&lt;/span&gt;  (mysql-5.7.22 ndb-7.6.6, Nodegroup: 0, *)
id=3    @&lt;span class="highlight"&gt;198.51.100.1&lt;/span&gt;  (mysql-5.7.22 ndb-7.6.6, Nodegroup: 0)

[ndb_mgmd(MGM)] 1 node(s)
id=1    @&lt;span class="highlight"&gt;198.51.100.2&lt;/span&gt;  (mysql-5.7.22 ndb-7.6.6)

[mysqld(API)]   1 node(s)
id=4    @&lt;span class="highlight"&gt;198.51.100.2&lt;/span&gt;  (mysql-5.7.22 ndb-7.6.6)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The above shows that there are two data nodes connected with &lt;code&gt;node-id&lt;/code&gt;s 2 and 3. There is also one management node with &lt;code&gt;node-id&lt;/code&gt; 1 and one MySQL server with &lt;code&gt;node-id&lt;/code&gt; 4. You can display more information about each id by typing its number with the command &lt;code&gt;STATUS&lt;/code&gt; as follows:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="ndb_mgm&amp;gt;"&gt;2 STATUS
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The above command shows you the status, MySQL version, and NDB version of node 2:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Node 2: started (mysql-5.7.22 ndb-7.6.6)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To exit the management console type &lt;code&gt;quit&lt;/code&gt;, and then hit &lt;code&gt;ENTER&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The management console is very powerful and gives you many other options for administering the cluster and its data, including creating an online backup. For more information consult the &lt;a href="https://dev.mysql.com/doc/refman/5.7/en/mysql-cluster-management.html"&gt;official MySQL documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;At this point, you’ve fully tested your MySQL Cluster installation. The concluding step of this guide shows you how to create and insert test data into this MySQL Cluster.&lt;/p&gt;

&lt;h2 id="step-5-—-inserting-data-into-mysql-cluster"&gt;Step 5 — Inserting Data into MySQL Cluster&lt;/h2&gt;

&lt;p&gt;To demonstrate the cluster’s functionality, let's create a new table using the NDB engine and insert some sample data into it. Note that in order to use cluster functionality, the engine must be specified explicitly as &lt;strong&gt;NDB&lt;/strong&gt;. If you use InnoDB (default) or any other engine, you will not make use of the cluster.&lt;/p&gt;

&lt;p&gt;First, let's create a database called &lt;code&gt;clustertest&lt;/code&gt; with the command:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;CREATE DATABASE clustertest;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, switch to the new database:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;USE clustertest;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, create a simple table called &lt;code&gt;test_table&lt;/code&gt; like this:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;CREATE TABLE test_table (name VARCHAR(20), value VARCHAR(20)) ENGINE=ndbcluster;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We have explicitly specified the engine &lt;code&gt;ndbcluster&lt;/code&gt; in order to make use of the cluster. &lt;/p&gt;

&lt;p&gt;Now, we can start inserting data using this SQL query:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;INSERT INTO test_table (name,value) VALUES('some_name','some_value');
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To verify that the data has been inserted, run the following select query:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="mysql&amp;gt;"&gt;SELECT * FROM test_table;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When you insert data into and select data from an &lt;code&gt;ndbcluster&lt;/code&gt; table, the cluster load balances queries between all the available data nodes. This improves the stability and performance of your MySQL database installation.&lt;/p&gt;

&lt;p&gt;You can also set the default storage engine to &lt;code&gt;ndbcluster&lt;/code&gt; in the &lt;code&gt;my.cnf&lt;/code&gt; file that we edited previously. If you do this, you won’t need to specify the &lt;code&gt;ENGINE&lt;/code&gt; option when creating tables. To learn more, consult the MySQL &lt;a href="https://dev.mysql.com/doc/refman/5.7/en/storage-engine-setting.html"&gt;Reference Manual&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;In this tutorial, we’ve demonstrated how to set up and configure a MySQL Cluster on Ubuntu 18.04 servers. It’s important to note that this is a minimal, pared-down architecture used to demonstrate the installation procedure, and there are many advanced options and features worth learning about before deploying MySQL Cluster in production (for example, performing backups). To learn more, consult the official &lt;a href="https://dev.mysql.com/doc/refman/5.7/en/mysql-cluster.html"&gt;MySQL Cluster  documentation&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/node-js-ubuntu-18-04-ru</id>
    <published>2018-07-26T20:22:02Z</published>
    <updated>2018-07-26T20:22:12Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/node-js-ubuntu-18-04-ru"/>
    <title>Как установить Node.js в Ubuntu 18.04</title>
    <content type="html">&lt;h3 id="Введение"&gt;Введение&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://nodejs.org/en/"&gt;Node.js&lt;/a&gt; это платформа для разработки приложений на языке JavaScript, позволяющая быстро создавать приложения, работающие в сети. Использование JavaScript одновременно на клиентской и на серверной стороне позволяет сделать разработку более унифицированной и спроектированной в рамках единой системы.&lt;/p&gt;

&lt;p&gt;В этом руководстве мы покажем вам, как начать использовать Node.js на сервере с Ubuntu 18.04.&lt;/p&gt;

&lt;h2 id="Перед-установкой"&gt;Перед установкой&lt;/h2&gt;

&lt;p&gt;Инструкции в этом руководстве предполагают, что вы используете Ubuntu 18.04. Перед началом убедитесь, что у вас есть не-рутовый пользователь с привилегиями &lt;code&gt;sudo&lt;/code&gt;. Настроить такого пользователя вы можете с помощью инструкций в статье &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-18-04"&gt;Начальная настройка сервера на Ubuntu 18.04&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id="Установка-стабильной-версии-дистрибутива-из-стандартных-репозиториев"&gt;Установка стабильной версии дистрибутива из стандартных репозиториев&lt;/h2&gt;

&lt;p&gt;В стандартных репозиториях Ubuntu 18.04 есть версия Node.js, которую удобно использовать для обеспечения однородной среды выполнения сетевых приложений сразу на нескольких серверах. На момент написания этой статьи текущая версия в репозиториях - 8.10.0. Это не самая последняя версия, но она довольно стабильна и её будет достаточно для экспериментов с языком.&lt;/p&gt;

&lt;p&gt;Для установки этой версии воспользуемся пакетным менеджером &lt;code&gt;apt&lt;/code&gt;. Сначала обновим локальный индекс пакетов:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt update
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Теперь установим Node.js из репозиториев:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install nodejs
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Если пакет из репозиториев удовлетворяет вашим потребностям, то на этом установка Node.js закончена. Однако в большинстве случаев вам также потребуется установить &lt;code&gt;npm&lt;/code&gt; - менеджер пакетов для Node.js. Это можно сделать при помощи следующей команды:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install npm
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Это позволит вам легко устанавливать модули и пакеты для Node.js.&lt;/p&gt;

&lt;p&gt;Из-за конфликта с другим пакетом, исполняемый файл из репозиториев Ubuntu называется &lt;code&gt;nodejs&lt;/code&gt; вместо &lt;code&gt;node&lt;/code&gt;. При работе имейте это ввиду.&lt;/p&gt;

&lt;p&gt;Для проверки того, какую именно версию Node.js вы установили в процессе, описанном выше, выполните команду:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nodejs -v
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;После того, как вы определили версию Node.js, установленную из репозиториев Ubuntu, вы можете решить, хотите ли вы работать с разными версиям, архивами пакетов или менеджерами версий. Далее мы рассмотрим эти вопросы вместе с процессом более гибкой установки.&lt;/p&gt;

&lt;h2 id="Установка-при-помощи-ppa"&gt;Установка при помощи PPA&lt;/h2&gt;

&lt;p&gt;Альтернативный способ, при помощи которого можно установить более свежую версию Node.js, - это использование PPA (персональный архив пакетов), который поддерживается компанией NodeSource. В архиве содержатся более новые версии Node.js, чем в официальных репозиториях Ubuntu. Используя архив вы также сможете выбирать между Node.js v6.x (поддерживается до апреля 2019), Node.js v8.x (текущая версия с долгосрочной поддержкой до декабря 2019) и Node.js v10.x (последняя версия, поддерживается до апреля 2021).&lt;/p&gt;

&lt;p&gt;Прежде всего, вам необходимо установить сам PPA для получения доступа к его содержимому. Убедитесь, что вы находитесь в своей домашней директории, а затем используйте &lt;code&gt;curl&lt;/code&gt; для получения установочного скрипта для необходимой вам версии, заменив &lt;code&gt;&lt;span class="highlight"&gt;8.x&lt;/span&gt;&lt;/code&gt; на необходимую вам версию:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cd ~
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;curl -sL https://deb.nodesource.com/setup_&lt;span class="highlight"&gt;8.x&lt;/span&gt; -o nodesource_setup.sh
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Вы можете просмотреть содержимое скрипта с помощью &lt;code&gt;nano&lt;/code&gt; (или любого другого текстового редактора):&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nano nodesource_setup.sh
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Запустите скрипт с правами &lt;code&gt;sudo&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo bash nodesource_setup.sh
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;PPA будет включен в конфигурацию и ваш локальный кэш пакетов обновится автоматически. После выполнения установочного скрипта от Nodesource, вы можете установить Node.js так же, как описано ранее:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install nodejs
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Для проверки того, какую именно версию Node.js вы установили в процессе, описанном выше, выполните команду:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nodejs -v
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Вывод"&gt;Вывод&lt;/div&gt;v8.11.1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Пакет &lt;code&gt;nodejs&lt;/code&gt; содержит и &lt;code&gt;nodejs&lt;/code&gt; и &lt;code&gt;npm&lt;/code&gt;, поэтому нет никакой необходимости в дополнительной установке &lt;code&gt;npm&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm&lt;/code&gt; использует конфигурационный файл в вашей домашней директории для отслеживания обновлений. Этот файл будет создан при первом запуске &lt;code&gt;npm&lt;/code&gt;. Выполните следующую команду для того, чтобы убедиться, что &lt;code&gt;npm&lt;/code&gt; установлен, а также для создания конфигурационного файла:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;npm -v
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Вывод"&gt;Вывод&lt;/div&gt;5.6.0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Для работы некоторых пакетов из &lt;code&gt;npm&lt;/code&gt; (например таких, которые требуют компиляцию из исходников), вам потребуется установить пакет &lt;code&gt;build-essentials&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install build-essential
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Теперь у вас есть все необходимые инструменты для работы с пакетами &lt;code&gt;npm&lt;/code&gt;, которые требуют компиляции из исходников.&lt;/p&gt;

&lt;h2 id="Установка-при-помощи-nvm"&gt;Установка при помощи NVM&lt;/h2&gt;

&lt;p&gt;Альтернативой установке Node.js через &lt;code&gt;apt&lt;/code&gt; является использование специального инструмента &lt;code&gt;nvm&lt;/code&gt;, что расшифровывается как "Node.js version manager" (менеджер версий Node.js). Вместо того, чтобы работать на уровне операционной системы, &lt;code&gt;nvm&lt;/code&gt; работает на уровне независимой директории в вашей домашней директории. Это означает, что вы можете устанавливать несколько самостоятельных версий Node.js, которые не будут влиять друг на друга.&lt;/p&gt;

&lt;p&gt;Контроль вашей среды разработки посредством &lt;code&gt;nvm&lt;/code&gt; позволяет вам получить доступ к последним версиям Node.js, сохраняя при этом предыдущие версии. Эта утилита, тем не менее, отличается от &lt;code&gt;apt&lt;/code&gt;, и версии Node.js, которыми вы управляете с её помощью, отличаются от стабильных версий из стандартных репозиториев Ubuntu.&lt;/p&gt;

&lt;p&gt;Для загрузки установочного скрипта &lt;code&gt;nvm&lt;/code&gt; со &lt;a href="https://github.com/creationix/nvm"&gt;страницы проекта на GitHub&lt;/a&gt; можно использовать &lt;code&gt;curl&lt;/code&gt;. Обратите внимание на то, что номер версии может отличаться от указанного в этом примере:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;curl -sL https://raw.githubusercontent.com/creationix/nvm/&lt;span class="highlight"&gt;v0.33.11&lt;/span&gt;/install.sh -o install_nvm.sh
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Просмотреть установочный скрипт можно используя &lt;code&gt;nano&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nano install_nvm.sh
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Запустите скрипт в &lt;code&gt;bash&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;bash install_nvm.sh
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Эта команда установит ПО в поддиректорию &lt;code&gt;~/.nvm&lt;/code&gt; вашей домашней директории. Также в файл &lt;code&gt;~/.profile&lt;/code&gt; будут добавлены некоторые необходимые для работы настройки.&lt;/p&gt;

&lt;p&gt;Для получения доступа к функционалу &lt;code&gt;nvm&lt;/code&gt;, вам необходимо перелогиниться в системе, либо вы можете использовать команду &lt;code&gt;source&lt;/code&gt; для того, чтобы применить изменения не прерывая текущую сессию:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;source ~/.profile
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Теперь, когда &lt;code&gt;nvm&lt;/code&gt; установлен, вы можете устанавливать изолированные версии Node.js. Чтобы узнать, какие версии Node.js доступны для установки, наберите:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nvm ls-remote
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Вывод"&gt;Вывод&lt;/div&gt;...
         v8.11.1   (Latest LTS: Carbon)
         v9.0.0
         v9.1.0
         v9.2.0
         v9.2.1
         v9.3.0
         v9.4.0
         v9.5.0
         v9.6.0
         v9.6.1
         v9.7.0
         v9.7.1
         v9.8.0
         v9.9.0
        v9.10.0
        v9.10.1
        v9.11.0
        v9.11.1
        v10.0.0  
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Как вы можете видеть, новейшей версией на момент написания руководства является v8.11.1. Установить ее можно при помощи следующей команды:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nvm install &lt;span class="highlight"&gt;8.11.1&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Обычно &lt;code&gt;nvm&lt;/code&gt; переключается на использование последней установленной версии. Вы можете указать &lt;code&gt;nvm&lt;/code&gt; использовать только что загруженную версию в явном виде следующим образом:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nvm use &lt;span class="highlight"&gt;8.11.1&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Если вы устанавливаете Node.js через &lt;code&gt;nvm&lt;/code&gt;, исполняемый файл будет иметь имя &lt;code&gt;node&lt;/code&gt;. Посмотреть, какую версию в данный момент использует shell, можно при помощи команды:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;node -v
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Вывод"&gt;Вывод&lt;/div&gt;v8.11.1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Если у вас установлено несколько версий Node.js, посмотреть их список можно с помощью команды:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nvm ls
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Если вы хотите настроить одну из версий как версию по умолчанию, введите:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nvm alias default &lt;span class="highlight"&gt;8.11.1&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Эта версия будет автоматически выбираться при начале новой сессии. Вы также можете ссылаться на нее по псевдониму (алиасу) следующим образом:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nvm use default
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Каждая версия Node.js имеет свои собственные пакеты, управлять которыми можно при помощи &lt;code&gt;npm&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm&lt;/code&gt; может устанавливать пакеты в директорию &lt;code&gt;./node_modules&lt;/code&gt; проектов Node.js. Например, для модуля &lt;code&gt;express&lt;/code&gt; это можно сделать вот так:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;npm install &lt;span class="highlight"&gt;express&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Если вы хотите установить пакет глобально (чтобы он был доступен для других проектов, использующих ту же версию Node.js), следует добавить флаг &lt;code&gt;-g&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;npm install -g &lt;span class="highlight"&gt;express&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Эта команда установит пакет в директорию:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;~/.nvm/versions/node/&lt;span class="highlight"&gt;node_version&lt;/span&gt;/lib/node_modules/&lt;span class="highlight"&gt;express&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Глобальная установка позволит вам запускать команды из командной строки, но при этом вам придется использовать ссылку на пакет внутри вашего проекта следующим образом:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;npm link &lt;span class="highlight"&gt;express&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Узнать больше о доступных опциях можно при помощи следующей команды:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nvm help
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="Удаление-node-js"&gt;Удаление Node.js&lt;/h2&gt;

&lt;p&gt;Вы можете удалить Node.js используя &lt;code&gt;apt&lt;/code&gt; или &lt;code&gt;nvm&lt;/code&gt; в зависимости от того, что вы использовали для установки. Для удаление стабильной версии дистрибутива из репозиториев Ubuntu используйте &lt;code&gt;apt&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt remove nodejs
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Эта команда удалит пакет, но оставит конфигурационные файлы. Это может быть удобно, если вы захотите установить пакет ещё раз позднее. Если вы не хотите сохранять конфигурационные файлы для последующего использования, выполните команду:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt purge nodejs
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Эта команда удалит пакет и конфигурационные файлы, связанные с ним.&lt;/p&gt;

&lt;p&gt;Вы также можете удалить все неиспользуемые пакеты, которые были автоматически установлены при установке удалённого пакета:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt autoremove
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Для удаления версии Node.js, установленной с помощью &lt;code&gt;nvm&lt;/code&gt;, сперва определите, является ли эта версия текущей активной версией:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nvm current
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Если версия, которую вы хотите удалить, &lt;strong&gt;не&lt;/strong&gt; является текущей активной версией, выполните команду:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nvm uninstall &lt;span class="highlight"&gt;node_version&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Эта команда удалит выбранную версию Node.js.&lt;/p&gt;

&lt;p&gt;Если удаляемая версия является текущей активной версией, вам необходимо сначала деактивировать &lt;code&gt;nvm&lt;/code&gt; для применения ваших изменений:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nvm deactivate
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Теперь вы можете удалить текущую версию с использованием команды удаления выше, которая удалит все файлы, связанные с выбранной версией Node.js, за исключением кэшированных файлов, которые могут быть использованы при повторной установке.&lt;/p&gt;

&lt;h2 id="Заключение"&gt;Заключение&lt;/h2&gt;

&lt;p&gt;Как вы видите, существует несколько способов установки Node.js на ваш сервер с Ubuntu 18.04. Какой из этих способов подходит вам больше - решать вам. В то время, как установка из репозиториев Ubuntu - это наиболее простой метод, использование для установки &lt;code&gt;nvm&lt;/code&gt; является куда более гибким.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/nginx-let-s-encrypt-ubuntu-18-04-ru</id>
    <published>2018-07-26T20:14:46Z</published>
    <updated>2018-07-26T20:14:53Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/nginx-let-s-encrypt-ubuntu-18-04-ru"/>
    <title>Как повысить безопасность Nginx с помощью Let's Encrypt в Ubuntu 18.04</title>
    <content type="html">&lt;p&gt;&lt;em&gt;Предыдущая версия руководства была написана &lt;a href="https://www.digitalocean.com/community/users/hazelnut"&gt;Хэйзел Вирдо&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h3 id="Введение"&gt;Введение&lt;/h3&gt;

&lt;p&gt;Let's Encrypt представляет собой центр сертификации (Certificate Authority, CA), позволяющий получать и устанавливать бесплатные &lt;a href="https://www.digitalocean.com/community/tutorials/openssl-essentials-working-with-ssl-certificates-private-keys-and-csrs"&gt;сертификаты TLS/SSL&lt;/a&gt;, тем самым позволяя использовать шифрованный HTTPS на веб-серверах. Процесс получения сертификатов упрощается за счёт наличия клиента Certbot, который пытается автоматизировать большую часть (если не все) необходимых операций. В настоящее время весь процесс получения и установки сертификатов полностью автоматизирован и для Apache и для Nginx.&lt;/p&gt;

&lt;p&gt;В этом руководстве мы используем Certbot для получения бесплатного SSL сертификата для Nginx на Ubuntu 18.04, а также настроим автоматическое продление этого сертификата.&lt;/p&gt;

&lt;h2 id="Перед-установкой"&gt;Перед установкой&lt;/h2&gt;

&lt;p&gt;Перед тем, как начать следовать описанным в этой статье шагам, убедитесь, что у вас есть: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Сервер с Ubuntu 18.04, настроенный согласно &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-18-04"&gt;руководству по первичной настройке сервера с Ubuntu 18.04&lt;/a&gt;, включая настройку не-рутового (non-root) пользователя с привилегиями &lt;code&gt;sudo&lt;/code&gt; и настройку файрвола.&lt;/li&gt;
&lt;li&gt;Зарегистрированное доменное имя. В этом руководстве мы будем использовать &lt;strong&gt;example.com&lt;/strong&gt;. Вы можете приобрести доменное имя на &lt;a href="https://namecheap.com/"&gt;Namecheap&lt;/a&gt;, получить бесплатное доменное имя на &lt;a href="http://www.freenom.com/en/index.html"&gt;Freenom&lt;/a&gt; или использовать любой другой регистратор доменных имён.&lt;/li&gt;
&lt;li&gt;Для вашего сервера настроены обе записи DNS, указанные ниже. Для их настройки вы можете использовать наше &lt;a href="https://www.digitalocean.com/community/tutorials/an-introduction-to-digitalocean-dns"&gt;введение в работу с DNS в DigitalOcean&lt;/a&gt;.

&lt;ul&gt;
&lt;li&gt;Запись &lt;code&gt;A&lt;/code&gt; для &lt;code&gt;example.com&lt;/code&gt;, указывающая на публичный IP адрес вашего сервера.&lt;/li&gt;
&lt;li&gt;Запись &lt;code&gt;A&lt;/code&gt; для &lt;code&gt;www.&lt;span class="highlight"&gt;example.com&lt;/span&gt;&lt;/code&gt;, указывающая на публичный IP адрес вашего сервера.&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;Nginx, установленный согласно инструкциям из руководства &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-nginx-on-ubuntu-18-04"&gt;Как установить Nginx в Ubuntu 18.04&lt;/a&gt;. Убедитесь, что у вас есть настроенный &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-nginx-on-ubuntu-18-04#step-5-setting-up-server-blocks-(recommended)"&gt;серверный блок&lt;/a&gt; для вашего домена. В этом руководстве мы будем использовать &lt;code&gt;/etc/nginx/sites-available/&lt;span class="highlight"&gt;example.com&lt;/span&gt;&lt;/code&gt; в качестве примера.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="Шаг-1-Установка-certbot"&gt;Шаг 1 - Установка Certbot&lt;/h2&gt;

&lt;p&gt;Перед началом использования Let's Encrypt для получения SSL сертификаты установим Certbot на ваш сервер.&lt;/p&gt;

&lt;p&gt;Certbot находится в активной разработке, поэтому пакеты Certbot, предоставляемые Ubuntu, обычно являются устаревшими. Тем не менее, разработчики Certbot поддерживают свой репозиторий пакетов для Ubuntu с актуальными версиями, поэтому мы будем использовать именно этот репозиторий.&lt;/p&gt;

&lt;p&gt;Сначала добавим репозиторий:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo add-apt-repository ppa:certbot/certbot
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Далее нажмите &lt;code&gt;ENTER&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Установим пакет Certbot для Nginx с помощью &lt;code&gt;apt&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install python-certbot-nginx
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Теперь Certbot готов к использованию, но для того, чтобы он мог настроить SSL для Nginx, нам сперва необходимо проверить кое-какие настройки Nginx.&lt;/p&gt;

&lt;h2 id="Шаг-2-Проверка-настроек-nginx"&gt;Шаг 2 - Проверка настроек Nginx&lt;/h2&gt;

&lt;p&gt;Certbot должен иметь возможность найти корректный серверный блок в вашей конфигурации Nginx для того, чтобы автоматически конфигурировать SSL. Для этого он будет искать директиву &lt;code&gt;server_name&lt;/code&gt;, которая совпадает с доменным именем, для которого вы запросите сертификат.&lt;/p&gt;

&lt;p&gt;Если вы следовали инструкциям по &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-nginx-on-ubuntu-18-04#step-5-%E2%80%93-setting-up-server-blocks-recommended"&gt;настройке серверного блока в руководстве по установке Nginx&lt;/a&gt;, у вас должен быть серверный блок для вашего домена по адресу &lt;code&gt;/etc/nginx/sites-available/&lt;span class="highlight"&gt;example.com&lt;/span&gt;&lt;/code&gt; с уже правильно настроенной директивой &lt;code&gt;server_name&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Для проверки откройте файл серверного блока в &lt;code&gt;nano&lt;/code&gt; или любом другом текстовом редакторе:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/nginx/sites-available/&lt;span class="highlight"&gt;example.com&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Найдите строку с &lt;code&gt;server_name&lt;/code&gt;. Она должна выглядеть примерно так:&lt;/p&gt;
&lt;div class="code-label " title="/etc/nginx/sites-available/example.com"&gt;/etc/nginx/sites-available/example.com&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;...
server_name &lt;span class="highlight"&gt;example.com&lt;/span&gt; www.&lt;span class="highlight"&gt;example.com&lt;/span&gt;;
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Если она выглядит таким образом, закройте файл и переходите к следующему шагу.&lt;/p&gt;

&lt;p&gt;Если она не выглядит так, как описано выше, обновите директиву &lt;code&gt;server_name&lt;/code&gt;. Затем сохраните и закройте файл, после чего проверьте корректность синтаксиса вашего конфигурационного файла командой:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nginx -t
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Если вы получили ошибку, откройте файл серверного блока и проверьте его на наличие опечаток или пропущенных символов. После того, как ваш конфигурационный файл будет проходить проверку на корректность, перезагрузите Nginx для применения новой конфигурации:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl reload nginx
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Теперь Certbot может находить и обновлять корректный серверный блок.&lt;/p&gt;

&lt;p&gt;Далее обновим настройки файрвола для пропуска HTTPS трафика.&lt;/p&gt;

&lt;h2 id="Шаг-3-Разрешение-https-в-файрволе"&gt;Шаг 3 - Разрешение HTTPS в файрволе&lt;/h2&gt;

&lt;p&gt;Если у вас включен файрвол &lt;code&gt;ufw&lt;/code&gt;, как рекомендуется в руководстве по первичной настройке сервера, вам необходимо внести некоторые изменения в его настройки для разрешения трафика HTTPS. К счастью, Nginx регистрирует необходимые профили в &lt;code&gt;ufw&lt;/code&gt; в момент установки.&lt;/p&gt;

&lt;p&gt;Вы можете ознакомиться с текущими настройками командой:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw status
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Скорее всего вывод будет выглядеть следующим образом:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Вывод"&gt;Вывод&lt;/div&gt;Status: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere                  
Nginx HTTP                 ALLOW       Anywhere                  
OpenSSH (v6)               ALLOW       Anywhere (v6)             
Nginx HTTP (v6)            ALLOW       Anywhere (v6)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Как видно из вывода, разрешён только трафик HTTP.&lt;/p&gt;

&lt;p&gt;Для того, чтобы разрешить трафик HTTPS, разрешим профиль &lt;code&gt;Nginx Full&lt;/code&gt; и удалим избыточный профиль &lt;code&gt;Nginx HTTP&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw allow 'Nginx Full'
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo ufw delete allow 'Nginx HTTP'
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Проверим внесённые изменения:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw status
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Теперь настройки &lt;code&gt;ufw&lt;/code&gt; должны выглядеть следующим образом:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Вывод"&gt;Вывод&lt;/div&gt;Status: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere
Nginx Full                 ALLOW       Anywhere
OpenSSH (v6)               ALLOW       Anywhere (v6)
Nginx Full (v6)            ALLOW       Anywhere (v6)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Теперь мы можем запустить Certbot и получить наши сертификаты.&lt;/p&gt;

&lt;h2 id="Шаг-4-Получение-ssl-сертификата"&gt;Шаг 4 - Получение SSL сертификата&lt;/h2&gt;

&lt;p&gt;Certbot предоставляет несколько способов получения сертификатов SSL с использованием плагинов. Плагин для Nginx берёт на себя настройку Nginx и перезагрузку конфигурации, когда это необходимо. Для использования плагина выполним команду:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo certbot --nginx -d &lt;span class="highlight"&gt;example.com&lt;/span&gt; -d www.&lt;span class="highlight"&gt;example.com&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Эта команда запускает &lt;code&gt;certbot&lt;/code&gt; с плагином &lt;code&gt;--nginx&lt;/code&gt;, ключи &lt;code&gt;-d&lt;/code&gt; определяют имена доменов, для которых должен быть выпущен сертификат.&lt;/p&gt;

&lt;p&gt;Если это первый раз, когда вы запускаете &lt;code&gt;certbot&lt;/code&gt;, вам будет предложено ввести адрес электронной почты и согласиться с условиями использования сервиса. После этого &lt;code&gt;certbot&lt;/code&gt; свяжется с сервером Let's Encrypt, а затем проверит, что вы действительно контролируете домен, для которого вы запросили сертификат.&lt;/p&gt;

&lt;p&gt;Если всё прошло успешно, &lt;code&gt;certbot&lt;/code&gt; спросит, как вы хотите настроить конфигурацию HTTPS.&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Вывод"&gt;Вывод&lt;/div&gt;Please choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access.
-------------------------------------------------------------------------------
1: No redirect - Make no further changes to the webserver configuration.
2: Redirect - Make all requests redirect to secure HTTPS access. Choose this for
new sites, or if you're confident your site works on HTTPS. You can undo this
change by editing your web server's configuration.
-------------------------------------------------------------------------------
Select the appropriate number [1-2] then [enter] (press 'c' to cancel):
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Выберите подходящий вариант и нажмите &lt;code&gt;ENTER&lt;/code&gt;. Конфигурация будет обновлена, а Nginx перезапущен для применения изменений. &lt;code&gt;certbot&lt;/code&gt; выдаст сообщение о том, что процесс прошёл успешно, и где хранятся ваши сертификаты:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Вывод"&gt;Вывод&lt;/div&gt;IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/&lt;span class="highlight"&gt;example.com&lt;/span&gt;/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/&lt;span class="highlight"&gt;example.com&lt;/span&gt;/privkey.pem
   Your cert will expire on 2018-07-23. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot again
   with the "certonly" option. To non-interactively renew *all* of
   your certificates, run "certbot renew"
 - Your account credentials have been saved in your Certbot
   configuration directory at /etc/letsencrypt. You should make a
   secure backup of this folder now. This configuration directory will
   also contain certificates and private keys obtained by Certbot so
   making regular backups of this folder is ideal.
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ваши сертификаты загружены, установлены и работают. Попробуйте перезагрузить ваш сайт с использованием &lt;code&gt;https://&lt;/code&gt; и вы увидите значок безопасности в браузере. Он означает, что соединение с сайтом зашифровано, обычно он выглядит, как зелёная иконка замка. Если вы проверите ваш сервер тестом &lt;a href="https://www.ssllabs.com/ssltest/"&gt;SSL Labs Server Test&lt;/a&gt;, он получит оценку &lt;strong&gt;A&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Закончим тестированием процесса обновления сертификата.&lt;/p&gt;

&lt;h2 id="Шаг-5-Проверка-автоматического-обновления-сертификата"&gt;Шаг 5 - Проверка автоматического обновления сертификата&lt;/h2&gt;

&lt;p&gt;Сертификаты Let's Encrypt действительны только 90 дней. Это сделано для того, чтобы пользователи автоматизировали процесс обновления сертификатов. Пакет &lt;code&gt;certbot&lt;/code&gt;, который мы установили, делает это путём добавления скрипта обновления в &lt;code&gt;/etc/cron.d&lt;/code&gt;. Этот скрипт запускается раз в день и автоматически обновляет любые сертификаты, которые закончатся в течение ближайших 30 дней.&lt;/p&gt;

&lt;p&gt;Для тестирования процесса обновления мы можем сделать "сухой" запуск (dry run) &lt;code&gt;certbot&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo certbot renew --dry-run
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Если вы не видите каких-либо ошибок в результате выполнения этой команды, то всё в полном порядке. При необходимости Certbot будет обновлять ваши сертификаты и перезагружать Nginx для применения изменений. Если автоматическое обновление по какой-либо причине закончится ошибкой, Let's Encrypt отправит электронное письмо на указанный вами адрес электронной почты с информацией о сертификате, который скоро закончится.&lt;/p&gt;

&lt;h2 id="Заключение"&gt;Заключение&lt;/h2&gt;

&lt;p&gt;В этом руководстве мы рассмотрели процесс установки клиента Let's Encrypt &lt;code&gt;certbot&lt;/code&gt;, загрузили SSL сертификаты для вашего домена, настроили Nginx для использования этих сертификатов и настроили процесс автоматического обновления сертификатов. Если у вас есть вопросы по работе с Certbot, рекомендуем ознакомиться с &lt;a href="https://certbot.eff.org/docs/"&gt;документацией Certbot&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/como-instalar-e-utilizar-o-tensorflow-no-ubuntu-16-04-pt</id>
    <published>2018-07-26T19:18:39Z</published>
    <updated>2018-08-15T18:53:26Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/como-instalar-e-utilizar-o-tensorflow-no-ubuntu-16-04-pt"/>
    <title>Como Instalar e Utilizar o TensorFlow no Ubuntu 16.04</title>
    <content type="html">&lt;h3 id="introdução"&gt;Introdução&lt;/h3&gt;

&lt;p&gt;O &lt;a href="https://www.tensorflow.org/"&gt;TensorFlow&lt;/a&gt; é um software open-source para machine learning criado pelo Google para treinar redes neurais. As redes neurais do TensorFlow são expressas na forma de &lt;a href="https://www.tensorflow.org/programmers_guide/graphs"&gt;grafos de fluxo de dados com estado&lt;/a&gt;. Cada nó no gráfico representa a operação realizada por redes neurais em matrizes multidimensionais. Estas matrizes multidimensionais são comumente conhecidas como "tensors" ou "tensores", daí o nome TensorFlow.&lt;/p&gt;

&lt;p&gt;O TensorFlow é um sistema de software de &lt;a href="https://en.wikipedia.org/wiki/Deep_learning"&gt;deep learning&lt;/a&gt; ou aprendizagem profunda. O TensorFlow funciona bem para a recuperação de informações, conforme demonstrado pelo Google na forma como eles fazem a classificação em seu sistema de inteligência artificial de machine-learning, &lt;a href="https://en.wikipedia.org/wiki/RankBrain"&gt;RankBrain&lt;/a&gt;. O TensorFlow pode realizar reconhecimento de imagem, como mostrado no &lt;a href="https://arxiv.org/abs/1409.4842"&gt;Inception&lt;/a&gt; do Google, bem como reconhecimento da linguagem humana em áudio. Ele também é útil na solução de outros problemas não específicos para machine-learning, como as &lt;a href="https://www.tensorflow.org/tutorials/pdes"&gt;equações diferenciais parciais&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A arquitetura do TensorFlow permite a implantação em várias CPUs ou GPUs em um desktop, servidor ou dispositivo móvel. Existem também extensões para integração com &lt;a href="https://developer.nvidia.com/cuda-zone"&gt;CUDA&lt;/a&gt;, uma plataforma de computação paralela da Nvidia. Isso dá aos usuários que estão implantando 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.&lt;/p&gt;

&lt;p&gt;Neste tutorial, você vai instalar a versão "Suporte apenas à CPU" do TensorFlow. Essa instalação é ideal para pessoas que desejam instalar e usar o TensorFlow, mas que não possuem uma placa de vídeo Nvidia ou não precisam executar aplicações de desempenho crítico.&lt;/p&gt;

&lt;p&gt;Você pode instalar o TensorFlow de diversas formas. cada método tem um caso de uso e um ambiente de desenvolvimento diferentes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Python e Virtualenv&lt;/strong&gt;: Nessa abordagem, você instala o TensorFlow e todos os pacotes necessários para utilizar o TensorFow em um ambiente virtual Python. Isso isola o seu ambiente TensorFlow de outros programas em Python na mesma máquina.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;pip nativo&lt;/strong&gt;: Nesse método, você instala o TensorFlow globalmente em seu sistema. Isso é recomendado para pessoas que querem disponibilizar o TensorFlow para todos em um sistema multiusuário. Esse método de instalação não separa o TensorFlow em um ambiente isolado e pode interferir em outras instalações ou bibliotecas do Python.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Docker&lt;/strong&gt;: O Docker é um ambiente de execução de container e isola completamente o seu conteúdo dos pacotes preexistentes em seu sistema. Nesse método, você usa um container 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á esteja usando o Docker. No entanto, o tamanho da imagem do Docker será bem grande.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Neste tutorial, você vai instalar o TensorFlow em um ambiente virtual Python com o &lt;code&gt;virtualenv&lt;/code&gt;. Essa abordagem, isola a instalação do TensorFlow e coloca as coisas em funcionamento rapidamente. Depois de concluir a instalação, você fará a validação executando um pequeno programa do TensorFlow e, em seguida, usando o TensorFlow para executar o reconhecimento de imagem.&lt;/p&gt;

&lt;h2 id="pré-requisitos"&gt;Pré-requisitos&lt;/h2&gt;

&lt;p&gt;Antes de começar esse tutorial, você precisará do seguinte:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Um servidor Ubuntu 16.04 com pelo menos 1GB de RAM configurado seguindo o &lt;a href="https://www.digitalocean.com/community/tutorials/configuracao-inicial-de-servidor-com-ubuntu-16-04-pt"&gt;guia Configuração Inicial de servidor com Ubuntu 16.04&lt;/a&gt;, incluindo um usuário com privilégios sudo que não seja root e um firewall. Você precisará de pelo menos 1 GB de RAM para executar com sucesso o último exemplo neste tutorial. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Python 3.3 ou superior e o &lt;code&gt;virtualenv&lt;/code&gt; instalado. Siga &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-python-3-and-set-up-a-local-programming-environment-on-ubuntu-16-04"&gt;How to Install Python 3 on Ubuntu 16.04&lt;/a&gt; para configurar o Python e o &lt;code&gt;virtualenv&lt;/code&gt;. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Git instalado, o que você pode fazer seguindo  &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-git-on-ubuntu-16-04"&gt;How To Install Git on Ubuntu 16.04&lt;/a&gt;. Você usará isso para baixar um repositório de exemplos.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="passo-1-—-instalando-o-tensorflow"&gt;Passo 1 — Instalando o TensorFlow&lt;/h2&gt;

&lt;p&gt;Nesse passo vamos criar um ambiente virtual e instalar o TensorFlow.&lt;/p&gt;

&lt;p&gt;Primeiro, crie um diretório de projeto chamado &lt;code&gt;tf-demo&lt;/code&gt;: &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;mkdir ~/&lt;span class="highlight"&gt;tf-demo&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Navegue até o seu diretório &lt;code&gt;tf-demo&lt;/code&gt; recém criado:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cd ~/&lt;span class="highlight"&gt;tf-demo&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Em seguida crie um novo ambiente virtual chamado &lt;code&gt;tensorflow-dev&lt;/code&gt;. Execute o seguinte comando para criar o ambiente:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;python3 -m venv &lt;span class="highlight"&gt;tensorflow-dev&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Isso cria um novo diretório &lt;code&gt;tensorflow-dev&lt;/code&gt; que conterá todos os pacotes que você instalar enquanto esse ambiente estiver ativado. Ele também inclui o &lt;code&gt;pip&lt;/code&gt; e uma versão standalone do Python.&lt;/p&gt;

&lt;p&gt;Agora, ative seu ambiente virtual:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;source &lt;span class="highlight"&gt;tensorflow-dev&lt;/span&gt;/bin/activate
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Uma vez ativado, você verá algo semelhante a isso no seu terminal:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;(&lt;span class="highlight"&gt;tensorflow-dev&lt;/span&gt;)username@hostname:~/tf-demo $
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Agora, você pode instalar o TensorFlow em seu ambiente virtual.&lt;/p&gt;

&lt;p&gt;Execute o seguinte comando para instalar e atualizar para a versão mais nova do TensorFlow disponível no &lt;a href="https://pypi.python.org/pypi"&gt;PyPi&lt;/a&gt;:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="(tensorflow-dev)$"&gt;pip3 install --upgrade tensorflow
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;O TensorFlow irá instalar:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Collecting tensorflow
  Downloading tensorflow-1.4.0-cp36-cp36m-macosx_10_11_x86_64.whl (39.3MB)
    100% |████████████████████████████████| 39.3MB 35kB/s

...

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

&lt;/code&gt;&lt;/pre&gt;
&lt;span class='note'&gt;&lt;p&gt;
Se você quiser desativar seu ambiente virtual a qualquer momento, o comando é:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;deactivate
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Para reativar seu abiente mais tarde, navegue até o diretório do seu projeto e execute source &lt;span class="highlight"&gt;tensorflow-dev&lt;/span&gt;/bin/activate.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;&lt;/span&gt;

&lt;p&gt;Agora que você instalou o TensorFlow, vamos nos certificar de que a instalação dele está funcionando.&lt;/p&gt;

&lt;h2 id="passo-2-—-validando-a-instalação"&gt;Passo 2 — Validando a Instalação&lt;/h2&gt;

&lt;p&gt;Para validar a instalação do TensorFlow, vamos executar um programa simples nele como um usuário não-root. Vamos utilizar o clássico exemplo de iniciante "Hello, world!" como uma forma de validação. Em vez de criar um arquivo Python, criaremos esse programa usando o &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-work-with-the-python-interactive-console"&gt;Console Interativo do Python&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Para escrever o programa, inicie o seu interpretador Python:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="(tensorflow-dev)$"&gt;python
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Você verá o seguinte prompt aparecer em seu terminal:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&amp;gt;&amp;gt;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Esse é o prompt para o interpretador Python, e ele indica que está pronto para que você comece a digitar algumas declarações Python.&lt;/p&gt;

&lt;p&gt;Primeiro, digite essa linha para importar o pacote do TensorFlow e torná-lo disponível como a variável local &lt;code&gt;tf&lt;/code&gt;. Pressione &lt;code&gt;ENTER&lt;/code&gt; depois de digitar a linha de código:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="&amp;gt;&amp;gt;&amp;gt;"&gt;import tensorflow as tf
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Em seguida, adicione esta linha de código para definir a mensagem "Hello, world!":&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="&amp;gt;&amp;gt;&amp;gt;"&gt;hello = tf.constant("Hello, world!")
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Depois, crie uma nova sessão do TensorFlow e a atribua à variável &lt;code&gt;sess&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="&amp;gt;&amp;gt;&amp;gt;"&gt;sess = tf.Session()
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;span class='note'&gt;&lt;p&gt;
&lt;strong&gt;Nota&lt;/strong&gt;: Dependendo do seu ambiente, você poderá ver esta saída:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;2017-06-18 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.
2017-06-18 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.
2017-06-18 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.
2017-06-18 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.
2017-06-18 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.

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Isso lhe diz que você tem um &lt;a href="https://en.wikipedia.org/wiki/Instruction_set_architecture"&gt;instruction set&lt;/a&gt; que tem o potencial de ser otimizado para um desempenho melhor com o TensorFlow. Se você vir isso, poderá ignorá-lo com segurança e continuar.&lt;br&gt;&lt;/p&gt;&lt;/span&gt;

&lt;p&gt;Por fim, insira essa linha de código para imprimir o resultado da execução da sessão &lt;code&gt;hello&lt;/code&gt; do TensorFlow que você construiu em suas linhas de código anteriores:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="&amp;gt;&amp;gt;&amp;gt;"&gt;print(sess.run(hello))
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Você verá esta saída em seu console:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Hello, world!

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Isso indica que tudo está funcionando e que você pode começar a utilizar o TensorFlow para fazer algo mais interessante.&lt;/p&gt;

&lt;p&gt;Saia do console interativo do Python pressionando &lt;code&gt;CTRL+D&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Agora vamos usar a API de reconhecimento de imagem do TensorFlow para nos familiarizarmos mais com o TensorFlow.&lt;/p&gt;

&lt;h2 id="passo-3-—-usando-o-tensorflow-para-reconhecimento-de-imagem"&gt;Passo 3 — Usando o TensorFlow para Reconhecimento de Imagem&lt;/h2&gt;

&lt;p&gt;Agora que o TensorFlow está instalado e que você o validou através da execução de um programa simples, vamos dar uma olhada nos recursos de reconhecimento de imagem do TensorFlow.&lt;/p&gt;

&lt;p&gt;Para classificar uma imagem, você precisa treinar um modelo. Depois você precisa escrever algum código para usar o modelo. Para aprender mais sobre esses conceitos dê uma olhada em &lt;a href="https://www.digitalocean.com/community/tutorials/an-introduction-to-machine-learning"&gt;An Introduction to Machine Learning&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;O TensorFlow fornece um &lt;a href="https://github.com/tensorflow/models"&gt;repositório de modelos e exemplos&lt;/a&gt;, incluindo código e um modelo treinado para classificar imagens.&lt;/p&gt;

&lt;p&gt;Utilize o Git para clonar o repositório de modelos do TensorFlow a partir do GitHub dentro do seu diretório de projeto:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="(tensorflow-dev)$"&gt;git clone https://github.com/tensorflow/models.git
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Você verá a seguinte saída enquanto o Git baixa o repositório em uma nova pasta chamada &lt;code&gt;models&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Cloning into 'models'...
remote: Counting objects: 8785, done.
remote: Total 8785 (delta 0), reused 0 (delta 0), pack-reused 8785
Receiving objects: 100% (8785/8785), 203.16 MiB | 24.16 MiB/s, done.
Resolving deltas: 100% (4942/4942), done.
Checking connectivity... done.

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Vá para o diretório &lt;code&gt;models/tutorials/image/imagenet&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="(tensorflow-dev)$"&gt;cd models/tutorials/image/imagenet
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Este diretório contém o arquivo &lt;code&gt;classify_image.py&lt;/code&gt; que usa o TensorFlow para reconhecer imagens. Este programa faz o download de um modelo treinado de &lt;code&gt;tensorflow.org&lt;/code&gt; em sua primeira execução. O download desse modelo requer que você tenha 200 MB de espaço livre disponível no disco. &lt;/p&gt;

&lt;p&gt;Neste exemplo, vamos classificar uma &lt;a href="https://www.tensorflow.org/images/cropped_panda.jpg"&gt;imagem pré-fabricada de um Panda&lt;/a&gt;. Execute este comando para rodar o programa classificador de imagens:&lt;/p&gt;
&lt;pre class="code-pre custom_prefix"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="(tensorflow-dev)$"&gt;python classify_image.py
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Você verá uma saída semelhante a esta:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;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)

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Você classificou sua primeira imagem usando os recursos de reconhecimento de imagem do TensorFlow.&lt;/p&gt;

&lt;p&gt;Se você quiser usar outra imagem, faça isso adicionando o argumento &lt;code&gt;-- image_file&lt;/code&gt; ao seu comando &lt;code&gt;python3 classify_image.py&lt;/code&gt;. Para o argumento, você passaria o caminho absoluto do arquivo da imagem.&lt;/p&gt;

&lt;h2 id="conclusão"&gt;Conclusão&lt;/h2&gt;

&lt;p&gt;Você instalou o TensorFlow em um ambiente virtual Python e validou o funcionamento do TensorFlow executando alguns exemplos. Agora você possui ferramentas que o possibilitam a exploração de tópicos adicionais, incluindo &lt;a href="https://en.wikipedia.org/wiki/Convolutional_neural_network"&gt;Convolutional Neural Networks&lt;/a&gt; e &lt;a href="https://papers.nips.cc/paper/5021-distributed-representations-of-words-and-phrases-and-their-compositionality.pdf"&gt;Word Embeddings&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.tensorflow.org/programmers_guide/"&gt;O guia do programador&lt;/a&gt; do TensorFlow é um ótimo recurso e referência para o desenvolvimento nesse software. Você pode explorar o &lt;a href="https://www.kaggle.com/"&gt;Kaggle&lt;/a&gt;, um ambiente competitivo para 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 excelente &lt;a href="https://www.kaggle.com/wiki/Home"&gt;wiki&lt;/a&gt; onde você pode ver e compartilhar soluções, algumas das quais estão na vanguarda das técnicas estatísticas e de machine learning. &lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/servicos-de-armazenamento-de-objetos-versus-armazenamento-em-blocos-pt</id>
    <published>2018-07-26T19:05:21Z</published>
    <updated>2018-08-15T18:55:12Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/servicos-de-armazenamento-de-objetos-versus-armazenamento-em-blocos-pt"/>
    <title>Serviços de Armazenamento de Objetos versus Armazenamento em Blocos</title>
    <content type="html">&lt;h3 id="introdução"&gt;Introdução&lt;/h3&gt;

&lt;p&gt;O armazenamento de dados flexível e escalável é um requisito básico para a maioria dos aplicativos e serviços que estão sendo desenvolvidos com técnicas e ferramentas modernas. Seja armazenando grandes ou pequenas quantidades de imagens, vídeos ou pequenos blocos de texto, os desenvolvedores de aplicativos precisam de uma solução para o armazenamento e a recuperação do conteúdo gerado por usuários, logs, backups e assim por diante.&lt;/p&gt;

&lt;p&gt;Com os deployments complexos atuais, containers, e infraestrutura efêmera, os dias de simplesmente salvar arquivos no disco em um único servidor acabaram. Provedores de nuvem desenvolveram serviços para preencher as necessidades de armazenamento dos deployments de aplicações modernas, e eles se encaixam principalmente em duas categorias: armazenamento de objetos e armazenamento em blocos.&lt;/p&gt;

&lt;p&gt;Vamos dar uma olhada em ambos e discutir as vantagens, desvantagens e casos de uso para cada um.&lt;/p&gt;

&lt;h2 id="o-que-é-o-armazenamento-em-blocos"&gt;O que é o Armazenamento em Blocos&lt;/h2&gt;

&lt;p&gt;Os serviços de armazenamento em blocos ou block storage são relativamente simples e familiares. Eles fornecem um dispositivo de armazenamento em blocos tradicional — como um disco rígido — através da rede. Os provedores de nuvem geralmente têm produtos que podem provisionar um dispositivo de armazenamento em blocos de qualquer tamanho e anexá-lo à sua máquina virtual.&lt;/p&gt;

&lt;p&gt;A partir disso, você poderia tratá-lo como um disco normal. Você pode formatá-lo com um sistema de arquivos e armazenar arquivos nele, combinar vários dispositivos em um RAID, ou configurar um banco de dados para gravar diretamente no dispositivo de blocos, evitando completamente a sobrecarga do sistema de arquivos. Além disso, os dispositivos de armazenamento em blocos conectados à rede geralmente têm algumas vantagens exclusivas em relação aos discos rígidos normais:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Você pode tirar snapshots ou instantâneos ao vivo de todo o dispositivo para fins de backup&lt;/li&gt;
&lt;li&gt;Dispositivos de armazenamento em blocos podem ser redimensionados para acomodar as necessidades de crescimento&lt;/li&gt;
&lt;li&gt;Você pode facilmente desanexar e mover dispositivos de armazenamento em blocos entre as máquinas&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Esta é uma configuração muito flexível que pode ser útil para a maioria dos aplicativos de qualquer tipo. Vamos resumir algumas vantagens e desvantagens da tecnologia.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Algumas vantagens do armazenamento em blocos são:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;O Armazenamento em blocos é um paradigma familiar. Pessoas e softwares entendem e suportam arquivos e sistemas de arquivos quase que universalmente&lt;/li&gt;
&lt;li&gt;Dispositivos de blocos são bem suportados. Toda linguagem de programação pode ler e gravar arquivos facilmente&lt;/li&gt;
&lt;li&gt;Permissões de sistema de arquivos e controles de acesso são familiares e bem entendidos&lt;/li&gt;
&lt;li&gt;Os dispositivos de armazenamento em bloco fornecem I/O de baixa latência, sendo então, adequados para uso por bancos de dados.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;As desvantagens do armazenamento em blocos são:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;O armazenamento em blocos está ligado a um servidor de cada vez&lt;/li&gt;
&lt;li&gt;Blocos e sistemas de arquivos tem metadados limitados sobre os blobs de informações que eles estão armazenando (data da criação, proprietário, tamanho). Qualquer informação adicional sobre o que você está armazenando tem que ser tratada no nível da aplicação e do banco de dados, o que é uma complexidade adicional para um desenvolvedor se preocupar&lt;/li&gt;
&lt;li&gt;Você precisa pagar por todo o espaço de armazenamento em blocos que você alocou, mesmo que você não o esteja usando&lt;/li&gt;
&lt;li&gt;Você só pode acessar o armazenamento em blocos através de um servidor em execução&lt;/li&gt;
&lt;li&gt;O armazenamento em blocos precisa de mais trabalho e configuração manual se comparado ao armazenamento de objetos (escolha de sistemas de arquivos, permissões, versionamento, backups, etc).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Devido às suas características de I/O rápida, os serviços de armazenamento em blocos são adequados para armazenar dados em bancos de dados tradicionais. Além disso, muitos aplicativos legados que exigem armazenamento normal do sistema de arquivos precisarão usar um dispositivo de armazenamento em blocos.&lt;/p&gt;

&lt;p&gt;Se o seu provedor de nuvem não oferece um serviço de armazenamento em blocos, você pode executar o seu próprio serviço usando &lt;a href="https://www.openstack.org/software/releases/ocata/components/cinder"&gt;OpenStack Cinder&lt;/a&gt;, &lt;a href="http://ceph.com/"&gt;Ceph&lt;/a&gt;, ou o serviço iSCSI integrado disponível em muitos dispositivos NAS.&lt;/p&gt;

&lt;h2 id="o-que-é-o-armazenamento-de-objetos"&gt;O que é o Armazenamento de Objetos&lt;/h2&gt;

&lt;p&gt;No mundo moderno da computação em nuvem, o armazenamento de objetos ou object storage é o armazenamento e a recuperação de blobs (grandes objetos binários) não estruturados de dados e metadados utilizando uma API HTTP. Em vez da quebra dos arquivos em blocos para armazená-los em disco usando um sistema de arquivos, lidamos com objetos inteiros armazenados na rede. Esses objetos podem ser um arquivo de imagem, logs, arquivos HTML ou qualquer bloco de bytes auto contido. Eles são &lt;em&gt;não estruturados&lt;/em&gt; porque não há um esquema ou formato específico que eles precisem seguir.&lt;/p&gt;

&lt;p&gt;O Armazenamento de Objetos decolou porque simplificou muito a experiência do desenvolvedor. Como a API consiste de solicitações HTTP padrão, bibliotecas são rapidamente desenvolvidas para a maioria das linguagens de programação. O salvamento de um blob de dados tornou-se tão fácil quanto uma solicitação HTTP PUT ao object store. A recuperação de arquivo e metadados é uma solicitação GET normal. Além disso, a maioria dos serviços de armazenamento de objetos também pode servir os arquivos publicamente para seus usuários, eliminando a necessidade de manter um servidor web para hospedar recursos estáticos.&lt;/p&gt;

&lt;p&gt;Além do mais, os serviços de armazenamento de objetos cobram apenas pelo espaço de armazenamento que você usa (alguns também cobram por solicitação HTTP e por largura de banda de transferência). Isso é um benefício para pequenos desenvolvedores, que podem obter armazenamento de classe mundial e hospedagem de recursos a custos que aumentam com o uso.&lt;/p&gt;

&lt;p&gt;Entretanto, o armazenamento de objetos não é a solução ideal para todas as situações. Vamos olhar um resumo dos benefícios e desvantagens.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Algumas vantagens do armazenamento de objetos são:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Uma API HTTP simples, com clientes disponíveis para todos os principais sistemas operacionais e linguagens de programação &lt;/li&gt;
&lt;li&gt;Uma estrutura de custos na qual que você paga apenas pelo que usa&lt;/li&gt;
&lt;li&gt;Um serviço interno de publicação de recursos significando um servidor a menos que você precisa gerenciar&lt;/li&gt;
&lt;li&gt;Alguns provedores armazenamento de objetos oferecem integração com CDN, que armazena seus recursos em cache em todo o mundo para fazer downloads e carregamentos de página mais rápidos para seus usuários&lt;/li&gt;
&lt;li&gt;O versionamento opcional significa que você pode recuperar versões antigas de objetos para se proteger da sobrescrita acidental de dados&lt;/li&gt;
&lt;li&gt;Os serviços de armazenamento de objetos podem escalar facilmente de necessidades modestas para casos de uso realmente intensos, sem que o desenvolvedor tenha que lançar mais recursos ou rearquitetar a aplicação para lidar com a carga&lt;/li&gt;
&lt;li&gt;Usar um serviço de armazenamento de objetos significa que você não precisa manter discos rígidos e matrizes RAID, pois isso é feito pelo provedor de serviços.&lt;/li&gt;
&lt;li&gt;A capacidade de armazenar trechos de metadados junto com seu blob de dados pode simplificar ainda mais a arquitetura do seu aplicativo&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Algumas desvantagens do armazenamento de objetos são:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Você não pode utilizar serviços de armazenamento de objetos para manter um banco de dados tradicional, devido à alta latência desses serviços&lt;/li&gt;
&lt;li&gt;O armazenamento de objetos não permite que você altere apenas um fragmento de dados, você deve ler e escrever um objeto inteiro de uma só vez. Por exemplo, em um sistema de arquivos, você pode facilmente adicionar uma única linha ao final de um arquivo de log. Em um sistema de armazenamento de objetos, você precisaria recuperar o objeto, adicionar a nova linha e gravar todo o objeto de volta. Isso torna o armazenamento de objetos menos ideal para dados que mudam com muita frequência&lt;/li&gt;
&lt;li&gt;Sistemas operacionais não podem montar facilmente um armazenamento de objetos como um disco normal. Existem alguns clientes e adaptadores para ajudar nisso, mas em geral, usar e navegar em um armazenamento de objetos não é tão simples quanto folhear diretórios em um navegador de arquivos&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Devido a essas propriedades, o armazenamento de objetos é útil para hospedar recursos estáticos, salvamento de conteúdo criado por usuários tais como imagens e filmes, armazenamento de arquivos de backup, armazenamento de logs, por exemplo.&lt;/p&gt;

&lt;p&gt;Existem algumas soluções de armazenamento de objetos que você pode hospedar, embora você tenha que abrir mão de alguns dos benefícios de uma solução hospedada (como não ter que se preocupar com discos rígidos e problemas de dimensionamento). Você pode experimentar o &lt;a href="https://www.minio.io/"&gt;Minio&lt;/a&gt;, um popular servidor de armazenamento de objetos escrito na linguagem Go, o &lt;a href="http://ceph.com/"&gt;Ceph&lt;/a&gt;, ou o &lt;a href="https://www.openstack.org/software/releases/ocata/components/swift"&gt;OpenStack Swift&lt;/a&gt;. &lt;/p&gt;

&lt;h2 id="conclusão"&gt;Conclusão&lt;/h2&gt;

&lt;p&gt;A escolha de uma solução de armazenamento pode ser uma decisão complexa para desenvolvedores. Neste artigo discutimos as vantagens e desvantagens tanto dos serviços de armazenamento em blocos quanto dos serviços de armazenamento de objetos. É provável que qualquer aplicativo suficientemente complexo precisará dos dois tipos de armazenamento para atender a todas as suas necessidades.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/nginx-ubuntu-18-04-ru</id>
    <published>2018-07-23T21:54:45Z</published>
    <updated>2018-07-23T22:04:04Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/nginx-ubuntu-18-04-ru"/>
    <title>Как установить Nginx в Ubuntu 18.04</title>
    <content type="html">&lt;p&gt;&lt;em&gt;Предыдущая версия руководства была написана &lt;a href="https://www.digitalocean.com/community/users/jellingwood"&gt;Джастином Эллингвудом&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h3 id="Введение"&gt;Введение&lt;/h3&gt;

&lt;p&gt;Nginx является одним из самых популярных веб-серверов в мире, его используют для хостинга самых больших и нагруженных сайтов в Интернете. Nginx в подавляющем большинстве случаев менее требователен к ресурсам, чем Apache; его можно использовать как в качестве веб-сервера, так и в качестве обратного прокси-сервера (reverse proxy).&lt;/p&gt;

&lt;p&gt;В этом руководстве мы рассмотрим процесс установки Nginx на ваш сервер с Ubuntu 18.04.&lt;/p&gt;

&lt;h2 id="Перед-установкой"&gt;Перед установкой&lt;/h2&gt;

&lt;p&gt;Перед тем, как начать следовать описанным в этой статье шагам, убедитесь, что у вас есть обычный не-рутовый (non-root) пользователь с привилегиями &lt;code&gt;sudo&lt;/code&gt;. Узнать, как настроить такого пользователя на вашем сервере, можно из &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-18-04"&gt;статьи о первичной настройке сервера на Ubuntu 18.04&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;После того, как вы создали такого пользователя, зайдите на сервер используя его логин и пароль. Теперь вы готовы следовать шагам, описанным в этой статье.&lt;/p&gt;

&lt;h2 id="Шаг-1-Установка-веб-сервера-nginx"&gt;Шаг 1 - Установка веб-сервера Nginx&lt;/h2&gt;

&lt;p&gt;Nginx доступен в стандартных репозиториях Ubuntu, поэтому мы можем использовать менеджер пакетов &lt;code&gt;apt&lt;/code&gt; для его установки.&lt;/p&gt;

&lt;p&gt;Поскольку мы собираемся использовать &lt;code&gt;apt&lt;/code&gt; в первый раз в ходе этой сессии, начнём с обновления локального списка пакетов. Далее установим &lt;code&gt;nginx&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt update
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;sudo apt install nginx
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;В результате выполнения этих команд &lt;code&gt;apt&lt;/code&gt; установит Nginx и другие необходимые для его работы пакеты на ваш сервер.&lt;/p&gt;

&lt;h2 id="Шаг-2-Настройка-файрвола"&gt;Шаг 2 - Настройка файрвола&lt;/h2&gt;

&lt;p&gt;Перед тем, как начать проверять работу Nginx, нам необходимо настроить наш файрвол для разрешения доступа к сервису. При установки Nginx регистрируется в сервисе файрвола &lt;code&gt;ufw&lt;/code&gt;. Поэтому настройка доступа осуществляется достаточно просто.&lt;/p&gt;

&lt;p&gt;Для вывода настроек доступа для приложений, зарегистрированных в &lt;code&gt;ufw&lt;/code&gt;, введём команду:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw app list
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;В результате выполнения этой команды будет выведен список профилей приложений:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Вывод"&gt;Вывод&lt;/div&gt;Available applications:
  Nginx Full
  Nginx HTTP
  Nginx HTTPS
  OpenSSH
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Как видно из этого вывода, для Nginx настроено три профиля:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Nginx Full&lt;/strong&gt;: этот профиль открывает порты 80 (обычный, не шифрованный веб-трафик) и 443 (трафик шифруется с помощью TLS/SSL).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Nginx HTTP&lt;/strong&gt;: этот профиль открывает только порт 80 (обычный, не шифрованный веб-трафик).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Nginx HTTPS&lt;/strong&gt;: этот профиль открывает только порт 443 (трафик шифруется с помощью TLS/SSL).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Рекомендуется настраивать &lt;code&gt;ufw&lt;/code&gt; таким образом, чтобы разрешать только тот трафик, который вы хотите разрешить в явном виде. Поскольку мы ещё не настроили SSL для нашего сервера, в этой статье мы разрешим трафик только для порта 80.&lt;/p&gt;

&lt;p&gt;Сделать это можно следующей командой:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw allow 'Nginx HTTP'
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Вы можете проверить изменения введя команду:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw status
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;В результате должен отобразиться вывод следующего вида:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Вывод"&gt;Вывод&lt;/div&gt;Status: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere                  
Nginx HTTP                 ALLOW       Anywhere                  
OpenSSH (v6)               ALLOW       Anywhere (v6)             
Nginx HTTP (v6)            ALLOW       Anywhere (v6)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="Шаг-3-Проверка-работы-веб-сервера"&gt;Шаг 3 - Проверка работы веб-сервера&lt;/h2&gt;

&lt;p&gt;После завершения процесса установки Ubuntu 18.04 запустит Nginx автоматически. Таким образом веб-сервер уже должен быть запущен.&lt;/p&gt;

&lt;p&gt;Мы можем убедиться в этом выполнив следующую команду:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;systemctl status nginx
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Вывод"&gt;Вывод&lt;/div&gt;● nginx.service - A high performance web server and a reverse proxy server
   Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled)
   Active: &lt;span class="highlight"&gt;active (running)&lt;/span&gt; since Fri 2018-04-20 16:08:19 UTC; 3 days ago
     Docs: man:nginx(8)
 Main PID: 2369 (nginx)
    Tasks: 2 (limit: 1153)
   CGroup: /system.slice/nginx.service
           ├─2369 nginx: master process /usr/sbin/nginx -g daemon on; master_process on;
           └─2380 nginx: worker process
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Как видно из вывода выше, сервис запущен и работает. Тем не менее, убедимся в его полной работоспособности путём запроса веб-страницы.&lt;/p&gt;

&lt;p&gt;Для этого мы можем проверить, отображается ли веб-страница Nginx, доступная по умолчанию при вводе доменного имени или IP адреса сервера. Если вы не знаете публичного IP адреса сервера, вы можете найти этот IP адрес несколькими способами.&lt;/p&gt;

&lt;p&gt;Попробуйте набрать эту команду в терминале вашего сервера:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;ip addr show eth0 | grep inet | awk '{ print $2; }' | sed 's/\/.*$//'
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;В результате будет выведено несколько IP адресов. Попробуйте вставить каждый из них в браузер.&lt;/p&gt;

&lt;p&gt;Другим способом определить свой IP адрес будет проверка, как ваш сервер виден из Интернета:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;curl -4 icanhazip.com
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Наберите полученный IP адрес или доменное имя в вашем веб-браузере.&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;http://&lt;span class="highlight"&gt;IP_адрес_вашего_сервера&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Вы должны увидеть страницу Nginx по умолчанию.&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/nginx_1604/default_page.png" alt="Страница Nginx по умолчанию"&gt;&lt;/p&gt;

&lt;p&gt;Если вы видите подобную страницу в своём браузере, вы успешно установили Nginx.&lt;/p&gt;

&lt;h2 id="Шаг-4-Управление-процессом-nginx"&gt;Шаг 4 - Управление процессом Nginx&lt;/h2&gt;

&lt;p&gt;Теперь, когда Nginx установлен и мы убедились в его работоспособности, ознакомимся с некоторыми базовыми командам для управления нашим веб-сервером.&lt;/p&gt;

&lt;p&gt;Для остановки веб-сервера используйте команду:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl stop nginx
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Для запуска остановленного веб-сервера наберите:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl start nginx
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Для перезапуска веб-сервера можно использовать следующую команду:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl restart nginx
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Если вы вносите изменения в конфигурацию Nginx, часто можно перезапустить его без закрытия соединений. Для этого можно использовать следующую команду:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl reload nginx
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;По умолчанию Nginx настроен на автоматический старт при запуске сервера. Если такое поведение веб-сервера вам не нужно, вы можете отключить его следующей командой:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl disable nginx
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Для повторного включения запуска Nginx при старте сервера введите:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl enable nginx
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="Шаг-5-Настройка-серверных-блоков-рекомендуется"&gt;Шаг 5 - Настройка серверных блоков (рекомендуется)&lt;/h2&gt;

&lt;p&gt;При работе с Nginx серверный блоки (аналог виртуальных хостов в Apache) используются для инкапсуляции настроек сайтов и позволяют хостить более одного домена на сервере. Мы рассмотрим настройку серверных блоков на примере &lt;strong&gt;example.com&lt;/strong&gt;, но вам будет необходимо &lt;strong&gt;заменить этот домен своим реальным доменным именем&lt;/strong&gt;. Узнать больше о настройке доменных имён в DigitalOcean вы можете из нашего руководства &lt;a href="https://www.digitalocean.com/community/tutorials/an-introduction-to-digitalocean-dns"&gt;Введение в DNS DigitalOcean&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Nginx для Ubuntu 18.04 уже настроен для поддержки одного серверного блока, который настроен на показ документов из директории &lt;code&gt;/var/www/html&lt;/code&gt;. Несмотря на то, что это работает для одного сайта, это не очень удобно для хостинга нескольких сайтов. Вместо того, чтобы менять &lt;code&gt;/var/www/html&lt;/code&gt; создадим новую структуру директорий внутри &lt;code&gt;/var/www/&lt;/code&gt; для нашего сайта &lt;strong&gt;example.com&lt;/strong&gt;. Директорию &lt;code&gt;/var/www/html&lt;/code&gt; оставим без изменений, её содержимое будет отображаться, если клиентские запросы не подходят для отображения других настроенных на сервере сайтов.&lt;/p&gt;

&lt;p&gt;Создадим директорию для &lt;strong&gt;example.com&lt;/strong&gt; следующей командой, используя флаг &lt;code&gt;-p&lt;/code&gt; для создания любых необходимых родительских директорий:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo mkdir -p /var/www/&lt;span class="highlight"&gt;example.com&lt;/span&gt;/html
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Далее настроим права доступа для созданной директории для текущего пользователя, используя переменную окружения &lt;code&gt;$USER&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo chown -R $USER:$USER /var/www/&lt;span class="highlight"&gt;example.com&lt;/span&gt;/html
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Теперь права должны для корневой директории быть настроены правильным образом при условии, что вы не меняли своё значение &lt;code&gt;umask&lt;/code&gt;. На всяких случай мы можем удостовериться в этом командой:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo chmod -R 755 /var/www/&lt;span class="highlight"&gt;example.com&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Далее создадим страницу &lt;code&gt;index.html&lt;/code&gt; в &lt;code&gt;nano&lt;/code&gt; или любом другом текстовом редакторе:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nano /var/www/&lt;span class="highlight"&gt;example.com&lt;/span&gt;/html/index.html
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Добавим в файл следующий HTML:&lt;/p&gt;
&lt;div class="code-label " title="/var/www/example.com/html/index.html"&gt;/var/www/example.com/html/index.html&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&amp;lt;html&amp;gt;
    &amp;lt;head&amp;gt;
        &amp;lt;title&amp;gt;Welcome to &lt;span class="highlight"&gt;Example.com&lt;/span&gt;!&amp;lt;/title&amp;gt;
    &amp;lt;/head&amp;gt;
    &amp;lt;body&amp;gt;
        &amp;lt;h1&amp;gt;Success!  The &lt;span class="highlight"&gt;example.com&lt;/span&gt; server block is working!&amp;lt;/h1&amp;gt;
    &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Сохраните и закройте файл.&lt;/p&gt;

&lt;p&gt;Для того, чтобы Nginx мог отдавать этот контент, нам необходимо настроить серверный блок. Вместо того, чтобы редактировать существующий файл конфигурации серверного блока, создадим новый файл для нашего сайта - &lt;code&gt;/etc/nginx/sites-available/&lt;span class="highlight"&gt;example.com&lt;/span&gt;&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/nginx/sites-available/&lt;span class="highlight"&gt;example.com&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Скопируйте следующий текст настроек серверного блока в созданный файл:&lt;/p&gt;
&lt;div class="code-label " title="/etc/nginx/sites-available/example.com"&gt;/etc/nginx/sites-available/example.com&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;server {
        listen 80;
        listen [::]:80;

        root /var/www/&lt;span class="highlight"&gt;example.com&lt;/span&gt;/html;
        index index.html index.htm index.nginx-debian.html;

        server_name &lt;span class="highlight"&gt;example.com&lt;/span&gt; www.&lt;span class="highlight"&gt;example.com&lt;/span&gt;;

        location / {
                try_files $uri $uri/ =404;
        }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Обратите внимание на то, что мы изменили конфигурацию &lt;code&gt;root&lt;/code&gt; на адрес нашей новой директории, а &lt;code&gt;server_name&lt;/code&gt; на наше доменное имя.&lt;/p&gt;

&lt;p&gt;Теперь активируем файл путём создания ссылки на него в директории &lt;code&gt;sites-enabled&lt;/code&gt;, которую Nginx проверяет при старте:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ln -s /etc/nginx/sites-available/&lt;span class="highlight"&gt;example.com&lt;/span&gt; /etc/nginx/sites-enabled/
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Теперь два серверных блока активированы и настроены для ответа на основании своих директив &lt;code&gt;listen&lt;/code&gt; и &lt;code&gt;server_name&lt;/code&gt; (вы можете узнать больше о том, как Nginx обрабатывает эти директивы &lt;a href="https://www.digitalocean.com/community/tutorials/understanding-nginx-server-and-location-block-selection-algorithms"&gt;вот тут&lt;/a&gt;):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;example.com&lt;/code&gt;: Будет отвечать на запросы &lt;code&gt;example.com&lt;/code&gt; и &lt;code&gt;www.example.com&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;default&lt;/code&gt;: Будет отвечать на любые запросы на порту 80, которые не соответствуют другим настроенным блокам.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Для того, чтобы избежать потенциальной проблемы hash bucket memory, которая может появиться при добавлении дополнительных имён серверов, нам необходимо изменить одно значение в файле &lt;code&gt;/etc/nginx/nginx.conf&lt;/code&gt;. Откройте файл командой:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/nginx/nginx.conf
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Найдите директиву &lt;code&gt;server_names_hash_bucket_size&lt;/code&gt; и удалите символ &lt;code&gt;#&lt;/code&gt; для того, чтобы раскомментировать её:&lt;/p&gt;
&lt;div class="code-label " title="/etc/nginx/nginx.conf"&gt;/etc/nginx/nginx.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;...
http {
    ...
    server_names_hash_bucket_size 64;
    ...
}
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Далее проверим файлы Nginx на наличие синтаксических ошибок:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nginx -t
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Сохраните и закройте файл.&lt;/p&gt;

&lt;p&gt;Если никаких проблем не обнаружилось, перезапустите Nginx для применения внесённых изменений:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl restart nginx
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Теперь Nginx должен корректно обрабатывать ваше новое доменное имя. Вы можете убедиться в этом набрав в браузере &lt;code&gt;http://example.com&lt;/code&gt; и увидев что-то вроде такого вывода:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/nginx_server_block_1404/first_block.png" alt="Успешная настройка example.com"&gt;&lt;/p&gt;

&lt;h2 id="Шаг-6-Важные-файлы-и-директории-nginx"&gt;Шаг 6 - Важные файлы и директории Nginx&lt;/h2&gt;

&lt;p&gt;Теперь, когда мы знаем основные команды для управления веб-сервером, ознакомимся с основными директориями и файлами.&lt;/p&gt;

&lt;h3 id="Контент"&gt;Контент&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/var/www/html&lt;/code&gt;: веб-контент, который по умолчанию состоит только из тестовой страницы Nginx, которую мы видели ранее, находится в директории &lt;code&gt;/var/www/html&lt;/code&gt;. Путь к этой директории можно настроить в файлах конфигурации Nginx.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id="Конфигурация-сервера"&gt;Конфигурация сервера&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/etc/nginx&lt;/code&gt;: директория конфигурации Nginx. Все файлы конфигурации Nginx находятся в этой директории.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/etc/nginx/nginx.conf&lt;/code&gt;: основной файл конфигурации Nginx. Этот файл используется для внесения изменений в глобальную конфигурацию Nginx.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/etc/nginx/sites-available&lt;/code&gt;: директория, в которой хранятся серверные блоки для каждого сайта. Nginx не будет использовать конфигурационные файлы в этой директории, если они не имеют соответствующих ссылок в директории &lt;code&gt;sites-enabled&lt;/code&gt; (см. ниже). Обычно все настройки серверного блока осуществляются в этой директории, а затем сайт активируется путём создания ссылки в другой директории.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/etc/nginx/sites-enabled/&lt;/code&gt;: в этой директории хранятся серверные блоки для активированных сайтов. Обычно это достигается путём создания ссылок на конфигурационные профили сайтов, расположенные в директории &lt;code&gt;sites-available&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/etc/nginx/snippets&lt;/code&gt;: в этой директории хранятся фрагменты конфигурации, которые можно использовать при конфигурации любых сайтов. Фрагменты конфигурации, которые потенциально могут быть использованы в нескольких файлах конфигурации, являются прекрасными кандидатами для создания этих сниппетов.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id="Логи-сервера"&gt;Логи сервера&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/var/log/nginx/access.log&lt;/code&gt;: каждый запрос к вашему веб-серверу записывается в этот файл лога, если иное не задано настройками Nginx.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/var/log/nginx/error.log&lt;/code&gt;: любые ошибки Nginx будут записываться в этот файл.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="Заключение"&gt;Заключение&lt;/h2&gt;

&lt;p&gt;Теперь, когда у вас есть установленный и настроенный веб-сервер, вы можете выбирать, какой контент отдавать пользователям, и какие другие технологии вы можете использовать в дополнение к веб-серверу.&lt;/p&gt;

&lt;p&gt;Если вы хотите использовать более полный стек приложений, рекомендуем ознакомиться с нашим &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-linux-nginx-mysql-php-lemp-stack-ubuntu-18-04"&gt;руководством по настройке стека LEMP на сервере с Ubuntu 18.04&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-develop-a-node-js-tcp-server-application-using-pm2-and-nginx-on-ubuntu-16-04</id>
    <published>2018-07-20T15:17:55Z</published>
    <updated>2018-08-03T19:20:56Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-develop-a-node-js-tcp-server-application-using-pm2-and-nginx-on-ubuntu-16-04"/>
    <title>How To Develop a Node.js TCP Server Application using PM2 and Nginx on Ubuntu 16.04</title>
    <content type="html">&lt;p&gt;&lt;em&gt;The author selected &lt;a href="https://www.brightfunds.org/organizations/open-sourcing-mental-illness-ltd"&gt;OSMI&lt;/a&gt; to receive a donation as part of the &lt;a href="https://do.co/w4do-cta"&gt;Write for DOnations&lt;/a&gt; program.&lt;/em&gt;&lt;/p&gt;

&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://nodejs.org"&gt;Node.js&lt;/a&gt; is a popular open-source JavaScript runtime environment built on Chrome's V8 Javascript engine. Node.js is used for building server-side and networking applications.&lt;em&gt;TCP (Transmission Control Protocol)&lt;/em&gt; is a networking protocol that provides reliable, ordered and error-checked delivery of a stream of data between applications. A TCP server can accept a TCP connection request, and once the connection is established both sides can exchange data streams.&lt;/p&gt;

&lt;p&gt;In this tutorial, you'll build a basic Node.js TCP server, along with a client to test the server. You'll run your server as a background process using a powerful Node.js process manager called &lt;a href="http://pm2.keymetrics.io/"&gt;PM2&lt;/a&gt;. Then you'll configure &lt;a href="https://nginx.org/"&gt;Nginx&lt;/a&gt; as a reverse proxy for the TCP application and test the client-server connection from your local machine.&lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;To complete this tutorial, you will need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One Ubuntu 16.04 server set up by following &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-16-04"&gt;the Ubuntu 16.04 initial server setup guide&lt;/a&gt;, including a sudo non-root user and a firewall.&lt;/li&gt;
&lt;li&gt;Nginx installed on your server, as shown in &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-nginx-on-ubuntu-16-04"&gt;How To Install Nginx on Ubuntu 16.04&lt;/a&gt;. Nginx must be compiled with the &lt;code&gt;--with-stream&lt;/code&gt; option, which is the default on a fresh installation of Nginx through the &lt;code&gt;apt&lt;/code&gt; package manager on Ubuntu 16.04.&lt;/li&gt;
&lt;li&gt;Node.js installed using the official PPA, as explained in &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-node-js-on-ubuntu-16-04"&gt;How To Install Node.js on Ubuntu 16.04&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="step-1-—-creating-a-node-js-tcp-application"&gt;Step 1 — Creating a Node.js TCP Application&lt;/h2&gt;

&lt;p&gt;We will write a Node.js application using TCP Sockets. This is a sample application which will help you understand the &lt;a href="https://nodejs.org/api/net.html"&gt;Net&lt;/a&gt; library in Node.js which enables us to create raw TCP server and client applications.&lt;/p&gt;

&lt;p&gt;To begin, create a directory on your server in which you would like to place your Node.js application. For this tutorial, we will create our application in the  &lt;code&gt;~/tcp-nodejs-app&lt;/code&gt; directory:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;mkdir ~/tcp-nodejs-app
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then switch to the new directory:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cd ~/tcp-nodejs-app
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Create a new file named &lt;code&gt;package.json&lt;/code&gt; for your project. This file lists the packages that the application depends on. Creating this file will make the build reproducible as it will be easier to share this list of dependencies with other developers:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nano package.json
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can also generate the &lt;code&gt;package.json&lt;/code&gt; using the &lt;code&gt;npm init&lt;/code&gt; command, which will prompt you for the details of the application, but we'll still have to manually alter the file to add additional pieces, including a startup command. Therefore, we'll manually create the file in this tutorial.&lt;/p&gt;

&lt;p&gt;Add the following JSON to the file, which specifies the application's name, version, the main file, the command to start the application, and the software license:&lt;/p&gt;
&lt;div class="code-label " title="package.json"&gt;package.json&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-json"&gt;{
  "name": "tcp-nodejs-app",
  "version": "1.0.0",
  "main": "server.js",
  "scripts": {
    "start": "node server.js"
  },
  "license": "MIT"
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;scripts&lt;/code&gt; field lets you define commands for your application. The setting you specified here lets you run the app by running &lt;code&gt;npm start&lt;/code&gt; instead of running &lt;code&gt;node server.js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;package.json&lt;/code&gt; file can also contain a list of runtime and development dependencies, but we won't have any third party dependencies for this application.&lt;/p&gt;

&lt;p&gt;Now that you have the project directory and &lt;code&gt;package.json&lt;/code&gt; set up, let's create the server.&lt;/p&gt;

&lt;p&gt;In your application directory, create a &lt;code&gt;server.js&lt;/code&gt; file:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nano server.js
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Node.js provides a module called &lt;code&gt;net&lt;/code&gt; which enables TCP server and client communication. Load the &lt;code&gt;net&lt;/code&gt; module with &lt;code&gt;require()&lt;/code&gt;, then define variables to hold the port and host for the server: &lt;/p&gt;
&lt;div class="code-label " title="server.js"&gt;server.js&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-javascript"&gt;const net = require('net');
const port = 7070;
const host = '127.0.0.1';
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We'll use port &lt;code&gt;7070&lt;/code&gt; for this app,  but you can use any available port you'd like.  We're using  &lt;code&gt;127.0.0.1&lt;/code&gt; for the &lt;code&gt;HOST&lt;/code&gt; which ensures that our server is only listening on our local network interface. Later we will place Nginx in front of this app as a reverse proxy. Nginx is well-versed at handling multiple connections and horizontal scaling.&lt;/p&gt;

&lt;p&gt;Then add this code to spawn a TCP server using the &lt;code&gt;createServer()&lt;/code&gt; function from the &lt;code&gt;net&lt;/code&gt; module. Then start listening for connections on the port and host you defined by using the &lt;code&gt;listen()&lt;/code&gt; function of the &lt;code&gt;net&lt;/code&gt; module:&lt;/p&gt;
&lt;div class="code-label " title="server.js"&gt;server.js&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-javascript"&gt;...
const server = net.createServer();
server.listen(port, host, () =&amp;gt; {
    console.log('TCP Server is running on port ' + port +'.');
});

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save &lt;code&gt;server.js&lt;/code&gt; and start the server:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;npm start
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You'll see this output:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;TCP Server is running on port 7070
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The TCP server is running on port &lt;code&gt;7070&lt;/code&gt;.  Press &lt;code&gt;CTRL+C&lt;/code&gt; to stop the server.&lt;/p&gt;

&lt;p&gt;Now that we know the server is listening, let's write the code to handle client connections.&lt;/p&gt;

&lt;p&gt;When a client connects to the server, the server triggers a &lt;code&gt;connection&lt;/code&gt; event, which we'll observe.  We'll define an array of connected clients, which we'll call &lt;code&gt;sockets&lt;/code&gt;, and add each client instance to this array when the client connects. &lt;/p&gt;

&lt;p&gt;We'll use the &lt;code&gt;data&lt;/code&gt; event to process the data stream from the connected clients, using the &lt;code&gt;sockets&lt;/code&gt; array to broadcast data to all the connected clients.  &lt;/p&gt;

&lt;p&gt;Add this code to the &lt;code&gt;server.js&lt;/code&gt; file to implement these features:&lt;/p&gt;
&lt;div class="code-label " title="server.js"&gt;server.js&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-javascript"&gt;
...

let sockets = [];

server.on('connection', function(sock) {
    console.log('CONNECTED: ' + sock.remoteAddress + ':' + sock.remotePort);
    sockets.push(sock);

    sock.on('data', function(data) {
        console.log('DATA ' + sock.remoteAddress + ': ' + data);
        // Write the data back to all the connected, the client will receive it as data from the server
        sockets.forEach(function(sock, index, array) {
            sock.write(sock.remoteAddress + ':' + sock.remotePort + " said " + data + '\n');
        });
    });
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This tells the server to listen to &lt;code&gt;data&lt;/code&gt; events sent by connected clients. When the connected clients send any data to the server, we echo it back to all the connected clients by iterating through the &lt;code&gt;sockets&lt;/code&gt; array.&lt;/p&gt;

&lt;p&gt;Then add a handler for &lt;code&gt;close&lt;/code&gt; events which will be trigerred when a connected client terminates the connection. Whenever a client disconnects, we want to remove the client from the &lt;code&gt;sockets&lt;/code&gt; array so we no longer broadcast to it. Add this code at the end of the connection block:&lt;/p&gt;
&lt;div class="code-label " title="server.js"&gt;server.js&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-javascript"&gt;
let sockets = [];
server.on('connection', function(sock) {

    ...

    // Add a 'close' event handler to this instance of socket
    sock.on('close', function(data) {
        let index = sockets.findIndex(function(o) {
            return o.remoteAddress === sock.remoteAddress &amp;amp;&amp;amp; o.remotePort === sock.remotePort;
        })
        if (index !== -1) sockets.splice(index, 1);
        console.log('CLOSED: ' + sock.remoteAddress + ' ' + sock.remotePort);
    });
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here is the complete code for &lt;code&gt;server.js&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-label " title="server.js"&gt;server.js&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-javascript"&gt;const net = require('net');
const port = 7070;
const host = '127.0.0.1';

const server = net.createServer();
server.listen(port, host, () =&amp;gt; {
    console.log('TCP Server is running on port ' + port + '.');
});

let sockets = [];

server.on('connection', function(sock) {
    console.log('CONNECTED: ' + sock.remoteAddress + ':' + sock.remotePort);
    sockets.push(sock);

    sock.on('data', function(data) {
        console.log('DATA ' + sock.remoteAddress + ': ' + data);
        // Write the data back to all the connected, the client will receive it as data from the server
        sockets.forEach(function(sock, index, array) {
            sock.write(sock.remoteAddress + ':' + sock.remotePort + " said " + data + '\n');
        });
    });

    // Add a 'close' event handler to this instance of socket
    sock.on('close', function(data) {
        let index = sockets.findIndex(function(o) {
            return o.remoteAddress === sock.remoteAddress &amp;amp;&amp;amp; o.remotePort === sock.remotePort;
        })
        if (index !== -1) sockets.splice(index, 1);
        console.log('CLOSED: ' + sock.remoteAddress + ' ' + sock.remotePort);
    });
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save the file and then start the server again:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;npm start
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We have a fully functional TCP Server running on our machine. Next we'll write a client to connect to our server.&lt;/p&gt;

&lt;h2 id="step-2-—-creating-a-node-js-tcp-client"&gt;Step 2 — Creating a Node.js TCP Client&lt;/h2&gt;

&lt;p&gt;Our Node.js TCP Server is running, so let's create a TCP Client to connect to the server and test the server out.&lt;/p&gt;

&lt;p&gt;The Node.js server you just wrote is still running, blocking your current terminal session. We want to keep that running as we develop the client, so open a new Terminal window or tab. Then connect into the server again from the new tab. &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;ssh &lt;span class="highlight"&gt;sammy&lt;/span&gt;@&lt;span class="highlight"&gt;your_server_ip&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once connected, navigate to the &lt;code&gt;tcp-nodejs-app&lt;/code&gt; directory:&lt;/p&gt;
&lt;pre class="code-pre command second-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cd tcp-nodejs-app
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the same directory, create a new file called &lt;code&gt;client.js&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command second-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nano client.js
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The client will use the same &lt;code&gt;net&lt;/code&gt; library used in the &lt;code&gt;server.js&lt;/code&gt; file to connect to the TCP server.  Add this code to the file to connect to the server  using the IP address &lt;code&gt;127.0.0.1&lt;/code&gt; on port &lt;code&gt;7070&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-label " title="client.js"&gt;client.js&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-javascript"&gt;const net = require('net');
const client = new net.Socket();
const port = 7070;
const host = '127.0.0.1';

client.connect(port, host, function() {
    console.log('Connected');
    client.write("Hello From Client " + client.address().address);
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This code will first try to connect to the TCP server to ensure that the server we created is running. Once the connection is established, the client will send &lt;code&gt;"Hello From Client " + client.address().address&lt;/code&gt; to the server using the &lt;code&gt;client.write&lt;/code&gt; function. Our server will receive this data and echo it back to the client.  &lt;/p&gt;

&lt;p&gt;Once the client receives the data back from the server, we want it to print the server's response. Add this code to catch the &lt;code&gt;data&lt;/code&gt; event and print the server's response to the command line:&lt;/p&gt;
&lt;div class="code-label " title="client.js"&gt;client.js&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-javascript"&gt;client.on('data', function(data) {
    console.log('Server Says : ' + data);
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, handle disconnections from the server gracefully by adding this code:&lt;/p&gt;
&lt;div class="code-label " title="client.js"&gt;client.js&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-javascript"&gt;client.on('close', function() {
    console.log('Connection closed');
});

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save the &lt;code&gt;client.js&lt;/code&gt; file. &lt;/p&gt;

&lt;p&gt;Run the following command to start the client:&lt;/p&gt;
&lt;pre class="code-pre command second-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;node client.js
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The connection will establish and the server will recieve the data, echoing it back to the client:&lt;/p&gt;
&lt;pre class="code-pre  second-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="client.js Output"&gt;client.js Output&lt;/div&gt;Connected
Server Says : 127.0.0.1:34548 said Hello From Client 127.0.0.1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Switch back to the terminal where the server is running, and you'll see the following output:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="server.js Output"&gt;server.js Output&lt;/div&gt;CONNECTED: 127.0.0.1:34550
DATA 127.0.0.1: Hello From Client 127.0.0.1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You have verified that you can establish a TCP connection between your server and client apps. &lt;/p&gt;

&lt;p&gt;Press &lt;code&gt;CTRL+C&lt;/code&gt; to stop the server. Then switch to the other terminal session and press &lt;code&gt;CTRL+C&lt;/code&gt; to stop the client. You can now disconnect this terminal session from your server and return to your original terminal session.&lt;/p&gt;

&lt;p&gt;In the next step we'll launch the server with PM2 and run it in the background.&lt;/p&gt;

&lt;h2 id="step-3-—-running-the-server-with-pm2"&gt;Step 3 — Running the Server with PM2&lt;/h2&gt;

&lt;p&gt;You have a working server that accepts client connections, but it runs in the foreground. Let's run the server using PM2 so it runs in the backgrand and can restart gracefully.&lt;/p&gt;

&lt;p&gt;First, install PM2 on your server globally using  &lt;code&gt;npm&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo npm install pm2 -g
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once PM2 is installed, use it to run your server. Instead of running &lt;code&gt;npm start&lt;/code&gt; to start the server, you'll use the &lt;code&gt;pm2&lt;/code&gt; command. Start the server:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;pm2 start server.js
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You'll see output like this:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;[secondary_label Output
[PM2] Spawning PM2 daemon with pm2_home=/home/sammy/.pm2
[PM2] PM2 Successfully daemonized
[PM2] Starting /home/sammy/tcp-nodejs-app/server.js in fork_mode (1 instance)
[PM2] Done.
┌────────┬──────┬────────┬───┬─────┬───────────┐
│ Name   │ mode │ status │ ↺ │ cpu │ memory    │
├────────┼──────┼────────┼───┼─────┼───────────┤
│ server │ fork │ online │ 0 │ 5%  │ 24.8 MB   │
└────────┴──────┴────────┴───┴─────┴───────────┘
 Use `pm2 show &amp;lt;id|name&amp;gt;` to get more details about an app

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The server is now running in the background. However, if we reboot the machine, it won't be running anymore, so let's create a systemd service for it.&lt;/p&gt;

&lt;p&gt;Run the following command to generate and install PM2's systemd startup scripts. Be sure to run this with &lt;code&gt;sudo&lt;/code&gt; so the systemd files install automatically.&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo pm2 startup
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You'll see this output:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;[PM2] Init System found: systemd
Platform systemd

...

[PM2] Writing init configuration in /etc/systemd/system/pm2-root.service
[PM2] Making script booting at startup...
[PM2] [-] Executing: systemctl enable pm2-root...
Created symlink from /etc/systemd/system/multi-user.target.wants/pm2-root.service to /etc/systemd/system/pm2-root.service.
[PM2] [v] Command successfully executed.
+---------------------------------------+
[PM2] Freeze a process list on reboot via:
$ pm2 save

[PM2] Remove init script via:
$ pm2 unstartup systemd

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;PM2 is now running as a systemd service.&lt;/p&gt;

&lt;p&gt;You can list all the processes PM2 is managing with the &lt;code&gt;pm2 list&lt;/code&gt; command:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;pm2 list
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You'll see your application in the list, with the ID of &lt;code&gt;0&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;┌──────────┬────┬──────┬──────┬────────┬─────────┬────────┬─────┬───────────┬───────┬──────────┐
│ App name │ id │ mode │ pid  │ status │ restart │ uptime │ cpu │ mem       │ user  │ watching │
├──────────┼────┼──────┼──────┼────────┼─────────┼────────┼─────┼───────────┼───────┼──────────┤
│ server   │ &lt;span class="highlight"&gt;0&lt;/span&gt;  │ fork │ 9075 │ online │ 0       │ 4m     │ 0%  │ 30.5 MB   │ sammy │ disabled │
└──────────┴────┴──────┴──────┴────────┴─────────┴────────┴─────┴───────────┴───────┴──────────┘
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the preceding output, you'll notice that &lt;code&gt;watching&lt;/code&gt; is disabled.  This is a feature that reloads the server when you make a change to any of the application files. It's useful in development, but we don't need that feature in production.&lt;/p&gt;

&lt;p&gt;To get more info about any of the running processes, use the &lt;code&gt;pm2 show&lt;/code&gt; command, followed by its ID. In this case, the ID is &lt;code&gt;0&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;pm2 show &lt;span class="highlight"&gt;0&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This output shows the uptime, status, log file paths, and other info about the running application:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Describing process with id &lt;span class="highlight"&gt;0&lt;/span&gt; - name server
┌───────────────────┬──────────────────────────────────────────┐
│ status            │ online                                   │
│ name              │ server                                   │
│ restarts          │ 0                                        │
│ uptime            │ 7m                                       │
│ script path       │ /home/sammy/tcp-nodejs-app/server.js     │
│ script args       │ N/A                                      │
│ error log path    │ /home/sammy/.pm2/logs/server-error-0.log │
│ out log path      │ /home/sammy/.pm2/logs/server-out-0.log   │
│ pid path          │ /home/sammy/.pm2/pids/server-0.pid       │
│ interpreter       │ node                                     │
│ interpreter args  │ N/A                                      │
│ script id         │ 0                                        │
│ exec cwd          │ /home/sammy/tcp-nodejs-app               │
│ exec mode         │ fork_mode                                │
│ node.js version   │ 8.11.2                                   │
│ watch &amp;amp; reload    │ ✘                                        │
│ unstable restarts │ 0                                        │
│ created at        │ 2018-05-30T19:29:45.765Z                 │
└───────────────────┴──────────────────────────────────────────┘
Code metrics value
┌─────────────────┬────────┐
│ Loop delay      │ 1.12ms │
│ Active requests │ 0      │
│ Active handles  │ 3      │
└─────────────────┴────────┘
Add your own code metrics: http://bit.ly/code-metrics
Use `pm2 logs server [--lines 1000]` to display logs
Use `pm2 monit` to monitor CPU and Memory usage server

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If the application status shows an error, you can use the &lt;strong&gt;error log path&lt;/strong&gt; to open and review the error log to debug the error:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;cat /home/tcp/.pm2/logs/server-error-0.log 
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you make changes to the server code, you'll need to restart the application's process to apply the changes, like this:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;pm2 restart &lt;span class="highlight"&gt;0&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;PM2 is now managing the application. Now we'll use Nginx to proxy requests to the server.&lt;/p&gt;

&lt;h2 id="step-4-—-set-up-nginx-as-a-reverse-proxy-server"&gt;Step 4 — Set Up Nginx as a Reverse Proxy Server&lt;/h2&gt;

&lt;p&gt;Your application is running and listening on &lt;code&gt;127.0.0.1&lt;/code&gt;, which means it will only accept connections from the local machine. We will set up Nginx as a reverse proxy which will handle incoming traffic and direct it to our server.&lt;/p&gt;

&lt;p&gt;To do this, we'll modify the Nginx configuration to use the &lt;a href="https://nginx.org/en/docs/stream/ngx_stream_core_module.html#stream"&gt;&lt;code&gt;stream {}&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://nginx.org/en/docs/stream/ngx_stream_proxy_module.html"&gt;&lt;code&gt;stream_proxy&lt;/code&gt;&lt;/a&gt; features of Nginx to forward TCP connections to our Node.js server.&lt;/p&gt;

&lt;p&gt;We have to edit the main Nginx configuration file as the &lt;code&gt;stream&lt;/code&gt; block that configures TCP connection forwarding only works as a top-level block. The default Nginx configuration on Ubuntu loads server blocks within the &lt;code&gt;http&lt;/code&gt; block of the file, and the &lt;code&gt;stream&lt;/code&gt; block can't be placed within that block.&lt;/p&gt;

&lt;p&gt;Open the file &lt;code&gt;/etc/nginx/nginx.conf&lt;/code&gt; in your editor:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/nginx/nginx.conf
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add the following lines at the end of your configuration file:&lt;/p&gt;
&lt;div class="code-label " title="/etc/nginx/nginx.conf"&gt;/etc/nginx/nginx.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-nginx"&gt;
...

stream {
    server {
      listen 3000;
      proxy_pass 127.0.0.1:7070;        
      proxy_protocol on;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This listens for TCP connections on port &lt;code&gt;3000&lt;/code&gt; and proxies the requests to your Node.js server running on port &lt;code&gt;7070&lt;/code&gt;.  If your application is set to listen on a different port, update the proxy pass URL port to the correct port number. The &lt;code&gt;proxy_protocol&lt;/code&gt; directive tells Nginx to use the &lt;a href="https://docs.nginx.com/nginx/admin-guide/load-balancer/using-proxy-protocol/"&gt;PROXY protocol&lt;/a&gt; to send client information to backend servers, which can then process that information as needed.&lt;/p&gt;

&lt;p&gt;Save the file and exit the editor. &lt;/p&gt;

&lt;p&gt;Check your Nginx configuration to ensure you didn't introduce any syntax errors:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nginx -t
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, restart Nginx to enable the TCP and UDP proxy functionality:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl restart nginx
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next,  allow TCP connections to our server on that port. Use &lt;code&gt;ufw&lt;/code&gt; to allow connections on port &lt;code&gt;3000&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo sudo ufw allow 3000
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Assuming that your Node.js application is running, and your application and Nginx configurations are correct, you should now be able to access your application via the Nginx reverse proxy. &lt;/p&gt;

&lt;h2 id="step-5-—-testing-the-client-server-connection"&gt;Step 5 — Testing the Client-Server Connection&lt;/h2&gt;

&lt;p&gt;Let's test the server out by connecting to the TCP server from our local machine using the &lt;code&gt;client.js&lt;/code&gt; script. To do so, you'll need to download the &lt;code&gt;client.js&lt;/code&gt; file you developed to your local machine and change the port and IP address in the script.&lt;/p&gt;

&lt;p&gt;First, on your local machine, download the &lt;code&gt;client.js&lt;/code&gt; file using &lt;code&gt;scp&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;[environment local
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;scp &lt;span class="highlight"&gt;sammy&lt;/span&gt;@&lt;span class="highlight"&gt;your_server_ip&lt;/span&gt;:~/tcp-nodejs-app/client.js client.js
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Open the &lt;code&gt;client.js&lt;/code&gt; file in your editor:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;[environment local
&lt;/li&gt;&lt;li class="line" prefix="$"&gt;nano client.js
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Change the &lt;code&gt;port&lt;/code&gt; to &lt;code&gt;3000&lt;/code&gt; and change the &lt;code&gt;host&lt;/code&gt; to your server's IP address:&lt;/p&gt;
&lt;div class="code-label " title="client.js"&gt;client.js&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code class="code-highlight language-javascript"&gt;// A Client Example to connect to the Node.js TCP Server
const net = require('net');
const client = new net.Socket();
const port = &lt;span class="highlight"&gt;3000&lt;/span&gt;;
const host = '&lt;span class="highlight"&gt;your_server_ip&lt;/span&gt;';
...

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save the file, exit the editor, and test things out by running the client:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;node client.js 
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You'll see the same output you saw when you ran it before, indicating that your client machine has connected through Nginx and reached your server:&lt;/p&gt;
&lt;pre class="code-pre  local-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="client.js Output"&gt;client.js Output&lt;/div&gt;Connected
Server Says : 127.0.0.1:34584 said PROXY TCP4 &lt;span class="highlight"&gt;your_local_ip_address&lt;/span&gt; &lt;span class="highlight"&gt;your_server_ip&lt;/span&gt; 52920 3000
Hello From Client &lt;span class="highlight"&gt;your_local_ip_address&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since Nginx is proxying client connections to your server, your Node.js server won't see the real IP addresses of the clients; it will only see Nginx's IP address. Nginx doesn't support sending the real IP address to the backend directly without making some changes to your system that could impact security, but since we enabled the PROXY protocol in Nginx, the Node.js server is now receiving an additional &lt;code&gt;PROXY&lt;/code&gt; message that contains the real IP. If you need that IP address, you can adapt your server to process &lt;code&gt;PROXY&lt;/code&gt; requests and parse out the data you need.&lt;/p&gt;

&lt;p&gt;You now have your Node.js TCP application running behind an Nginx reverse proxy and can continue to develop your server further.&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;In this tutorial you created a TCP application with Node.js, ran it with PM2, and served it behind Nginx. You also created a client application to connect to it from other machines.  You can use this application to handle large chunks of data streams or to build real-time messaging applications.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-inspect-kubernetes-networking</id>
    <published>2018-07-16T19:48:29Z</published>
    <updated>2018-09-19T19:15:50Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-inspect-kubernetes-networking"/>
    <title>How To Inspect Kubernetes Networking</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;Kubernetes is a container orchestration system that can manage containerized applications across a cluster of server nodes. Maintaining network connectivity between all the containers in a cluster requires some advanced networking techniques. In this article, we will briefly cover some tools and techniques for inspecting this networking setup.&lt;/p&gt;

&lt;p&gt;These tools may be useful if you are debugging connectivity issues, investigating network throughput problems, or exploring Kubernetes to learn how it operates.&lt;/p&gt;

&lt;p&gt;If you want to learn more about Kubernetes in general, our guide &lt;a href="https://www.digitalocean.com/community/tutorials/an-introduction-to-kubernetes"&gt;&lt;em&gt;An Introduction to Kubernetes&lt;/em&gt;&lt;/a&gt; covers the basics. For a networking-specific overview of Kubernetes, please read &lt;a href="https://www.digitalocean.com/community/tutorials/kubernetes-networking-under-the-hood"&gt;&lt;em&gt;Kubernetes Networking Under the Hood&lt;/em&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id="getting-started"&gt;Getting Started&lt;/h2&gt;

&lt;p&gt;This tutorial will assume that you have a Kubernetes cluster, with &lt;code&gt;kubectl&lt;/code&gt; installed locally and configured to connect to the cluster.&lt;/p&gt;

&lt;p&gt;The following sections contain many commands that are intended to be run on a Kubernetes node. They will look like this:&lt;/p&gt;
&lt;pre class="code-pre super_user"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="#"&gt;echo 'this is a node command'
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Commands that should be run on your local machine will have the following appearance:&lt;/p&gt;
&lt;pre class="code-pre super_user local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="#"&gt;echo 'this is a local command'
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span class='note'&gt;&lt;strong&gt;Note:&lt;/strong&gt; Most of the commands in this tutorial will need to be run as the &lt;strong&gt;root&lt;/strong&gt; user. If you instead use a sudo-enabled user on your Kubernetes nodes, please add &lt;code&gt;sudo&lt;/code&gt; to run the commands when necessary.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;

&lt;h2 id="finding-a-pod-39-s-cluster-ip"&gt;Finding a Pod's Cluster IP&lt;/h2&gt;

&lt;p&gt;To find the cluster IP address of a Kubernetes pod, use the &lt;code&gt;kubectl get pod&lt;/code&gt; command on your local machine, with the option &lt;code&gt;-o wide&lt;/code&gt;. This option will list more information, including the node the pod resides on, and the pod's cluster IP.&lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl get pod -o wide
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre  local-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;NAME                           READY     STATUS    RESTARTS   AGE       IP            NODE
hello-world-5b446dd74b-7c7pk   1/1       Running   0          22m       10.244.18.4   node-one
hello-world-5b446dd74b-pxtzt   1/1       Running   0          22m       10.244.3.4    node-two
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;strong&gt;IP&lt;/strong&gt; column will contain the internal cluster IP address for each pod.&lt;/p&gt;

&lt;p&gt;If you don't see the pod you're looking for, make sure you're in the right namespace. You can list all pods in all namespaces by adding the flag &lt;code&gt;--all-namespaces&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id="finding-a-service-39-s-ip"&gt;Finding a Service's IP&lt;/h2&gt;

&lt;p&gt;We can find a Service IP using &lt;code&gt;kubectl&lt;/code&gt; as well. In this case we will list all services in all namespaces:&lt;/p&gt;
&lt;pre class="code-pre command local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;kubectl get service --all-namespaces
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre  local-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;NAMESPACE     NAME                       TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)         AGE
default       kubernetes                 ClusterIP   10.32.0.1       &amp;lt;none&amp;gt;        443/TCP         6d
kube-system   csi-attacher-doplugin      ClusterIP   10.32.159.128   &amp;lt;none&amp;gt;        12345/TCP       6d
kube-system   csi-provisioner-doplugin   ClusterIP   10.32.61.61     &amp;lt;none&amp;gt;        12345/TCP       6d
kube-system   kube-dns                   ClusterIP   10.32.0.10      &amp;lt;none&amp;gt;        53/UDP,53/TCP   6d
kube-system   kubernetes-dashboard       ClusterIP   10.32.226.209   &amp;lt;none&amp;gt;        443/TCP         6d
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The service IP can be found in the &lt;strong&gt;CLUSTER-IP&lt;/strong&gt; column.&lt;/p&gt;

&lt;h2 id="finding-and-entering-pod-network-namespaces"&gt;Finding and Entering Pod Network Namespaces&lt;/h2&gt;

&lt;p&gt;Each Kubernetes pod gets assigned its own network namespace. Network namespaces (or netns) are a Linux networking primitive that provide isolation between network devices.&lt;/p&gt;

&lt;p&gt;It can be useful to run commands from within a pod's netns, to check DNS resolution or general network connectivity. To do so, we first need to look up the process ID of one of the containers in a pod. For Docker, we can do that with a series of two commands. First, list the containers running on a node:&lt;/p&gt;
&lt;pre class="code-pre super_user"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="#"&gt;docker ps
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;CONTAINER ID        IMAGE                                   COMMAND                  CREATED             STATUS              PORTS               NAMES
&lt;span class="highlight"&gt;173ee46a3926&lt;/span&gt;        gcr.io/google-samples/node-hello        "/bin/sh -c 'node se…"   9 days ago          Up 9 days                               &lt;span class="highlight"&gt;k8s_hello-world_hello-world-5b446dd74b-pxtzt_default_386a9073-7e35-11e8-8a3d-bae97d2c1afd_0&lt;/span&gt;
11ad51cb72df        k8s.gcr.io/pause-amd64:3.1              "/pause"                 9 days ago          Up 9 days                               k8s_POD_hello-world-5b446dd74b-pxtzt_default_386a9073-7e35-11e8-8a3d-bae97d2c1afd_0
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Find the &lt;strong&gt;container ID&lt;/strong&gt; or &lt;strong&gt;name&lt;/strong&gt; of any container in the pod you're interested in. In the above output we're showing two containers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The first container is the &lt;code&gt;hello-world&lt;/code&gt; app running in the &lt;code&gt;hello-world&lt;/code&gt; pod&lt;/li&gt;
&lt;li&gt;The second is a &lt;em&gt;pause&lt;/em&gt; container running in the &lt;code&gt;hello-world&lt;/code&gt; pod. This container exists solely to hold onto the pod's network namespace&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To get the process ID of either container, take note of the container ID or name, and use it in the following &lt;code&gt;docker&lt;/code&gt; command:&lt;/p&gt;
&lt;pre class="code-pre super_user"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="#"&gt;docker inspect --format '{{ .State.Pid }}' &lt;span class="highlight"&gt;container-id-or-name&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;&lt;span class="highlight"&gt;14552&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A process ID (or PID) will be output. Now we can use the &lt;code&gt;nsenter&lt;/code&gt; program to run a command in that process's network namespace:&lt;/p&gt;
&lt;pre class="code-pre super_user"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="#"&gt;nsenter -t &lt;span class="highlight"&gt;your-container-pid&lt;/span&gt; -n &lt;span class="highlight"&gt;ip addr&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Be sure to use your own PID, and replace &lt;code&gt;ip addr&lt;/code&gt; with the command you'd like to run inside the pod's network namespace.&lt;/p&gt;

&lt;p&gt;&lt;span class='note'&gt;&lt;strong&gt;Note:&lt;/strong&gt; One advantage of using &lt;code&gt;nsenter&lt;/code&gt; to run commands in a pod's namespace – versus using something like &lt;code&gt;docker exec&lt;/code&gt; – is that you have access to all of the commands available on the node, instead of the typically limited set of commands installed in containers.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;

&lt;h2 id="finding-a-pod-39-s-virtual-ethernet-interface"&gt;Finding a Pod's Virtual Ethernet Interface&lt;/h2&gt;

&lt;p&gt;Each pod's network namespace communicates with the node's root netns through a virtual ethernet pipe. On the node side, this pipe appears as a device that typically begins with &lt;code&gt;veth&lt;/code&gt; and ends in a unique identifier, such as &lt;code&gt;veth77f2275&lt;/code&gt; or &lt;code&gt;veth01&lt;/code&gt;. Inside the pod this pipe appears as &lt;code&gt;eth0&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It can be useful to correlate which &lt;code&gt;veth&lt;/code&gt; device is paired with a particular pod. To do so, we will list all network devices on the node, then list the devices in the pod's network namespace. We can then correlate device numbers between the two listings to make the connection.&lt;/p&gt;

&lt;p&gt;First, run &lt;code&gt;ip addr&lt;/code&gt; in the pod's network namespace using &lt;code&gt;nsenter&lt;/code&gt;. Refer to the previous section &lt;a href="#finding-and-entering-pod-network-namespaces"&gt;&lt;em&gt;Finding and Entering Pod Network Namespaces&lt;/em&gt;&lt;br&gt;
&lt;/a&gt; for details on how to do this:&lt;/p&gt;
&lt;pre class="code-pre super_user"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="#"&gt;nsenter -t &lt;span class="highlight"&gt;your-container-pid&lt;/span&gt; -n ip addr
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;1: lo: &amp;lt;LOOPBACK,UP,LOWER_UP&amp;gt; mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
10: eth0@&lt;span class="highlight"&gt;if11&lt;/span&gt;: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1450 qdisc noqueue state UP group default
    link/ether 02:42:0a:f4:03:04 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 10.244.3.4/24 brd 10.244.3.255 scope global eth0
       valid_lft forever preferred_lft forever
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The command will output a list of the pod's interfaces. Note the &lt;code&gt;if11&lt;/code&gt; number after &lt;code&gt;eth0@&lt;/code&gt; in the example output. This means this pod's &lt;code&gt;eth0&lt;/code&gt; is linked to the node's 11th interface. Now run &lt;code&gt;ip addr&lt;/code&gt; in the node's default namespace to list out its interfaces:&lt;/p&gt;
&lt;pre class="code-pre super_user"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="#"&gt;ip addr
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;1: lo: &amp;lt;LOOPBACK,UP,LOWER_UP&amp;gt; mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever

. . .

7: veth77f2275@if6: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1450 qdisc noqueue master docker0 state UP group default
    link/ether 26:05:99:58:0d:b9 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet6 fe80::2405:99ff:fe58:db9/64 scope link
       valid_lft forever preferred_lft forever
9: vethd36cef3@if8: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1450 qdisc noqueue master docker0 state UP group default
    link/ether ae:05:21:a2:9a:2b brd ff:ff:ff:ff:ff:ff link-netnsid 1
    inet6 fe80::ac05:21ff:fea2:9a2b/64 scope link
       valid_lft forever preferred_lft forever
&lt;span class="highlight"&gt;11&lt;/span&gt;: &lt;span class="highlight"&gt;veth4f7342d&lt;/span&gt;@if10: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1450 qdisc noqueue master docker0 state UP group default
    link/ether e6:4d:7b:6f:56:4c brd ff:ff:ff:ff:ff:ff link-netnsid 2
    inet6 fe80::e44d:7bff:fe6f:564c/64 scope link
       valid_lft forever preferred_lft forever
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The 11th interface is &lt;code&gt;veth4f7342d&lt;/code&gt; in this example output. This is the virtual ethernet pipe to the pod we're investigating.&lt;/p&gt;

&lt;h2 id="inspecting-conntrack-connection-tracking"&gt;Inspecting Conntrack Connection Tracking&lt;/h2&gt;

&lt;p&gt;Prior to version 1.11, Kubernetes used iptables NAT and the conntrack kernel module to track connections. To list all the connections currently being tracked, use the &lt;code&gt;conntrack&lt;/code&gt; command:&lt;/p&gt;
&lt;pre class="code-pre super_user"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="#"&gt;conntrack -L
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To watch continuously for new connections, use the &lt;code&gt;-E&lt;/code&gt; flag:&lt;/p&gt;
&lt;pre class="code-pre super_user"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="#"&gt;conntrack -E
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To list conntrack-tracked connections to a particular destination address, use the &lt;code&gt;-d&lt;/code&gt; flag:&lt;/p&gt;
&lt;pre class="code-pre super_user"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="#"&gt;conntrack -L -d &lt;span class="highlight"&gt;10.32.0.1&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If your nodes are having issues making reliable connections to services, it's possible your connection tracking table is full and new connections are being dropped. If that's the case you may see messages like the following in your system logs:&lt;/p&gt;
&lt;div class="code-label " title="/var/log/syslog"&gt;/var/log/syslog&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;Jul 12 15:32:11 worker-528 kernel: &lt;span class="highlight"&gt;nf_conntrack: table full, dropping packet.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There is a sysctl setting for the maximum number of connections to track. You can list out your current value with the following command:&lt;/p&gt;
&lt;pre class="code-pre super_user"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="#"&gt;sysctl net.netfilter.nf_conntrack_max
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;net.netfilter.nf_conntrack_max = 131072
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To set a new value, use the &lt;code&gt;-w&lt;/code&gt; flag:&lt;/p&gt;
&lt;pre class="code-pre super_user"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="#"&gt;sysctl -w net.netfilter.nf_conntrack_max=&lt;span class="highlight"&gt;198000&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To make this setting permanent, add it to the &lt;code&gt;sysctl.conf&lt;/code&gt; file:&lt;/p&gt;
&lt;div class="code-label " title="/etc/sysctl.conf"&gt;/etc/sysctl.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;. . .
&lt;span class="highlight"&gt;net.ipv4.netfilter.ip_conntrack_max = 198000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="inspecting-iptables-rules"&gt;Inspecting Iptables Rules&lt;/h2&gt;

&lt;p&gt;Prior to version 1.11, Kubernetes used iptables NAT to implement virtual IP translation and load balancing for Service IPs.&lt;/p&gt;

&lt;p&gt;To dump all iptables rules on a node, use the &lt;code&gt;iptables-save&lt;/code&gt; command:&lt;/p&gt;
&lt;pre class="code-pre super_user"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="#"&gt;iptables-save
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Because the output can be lengthy, you may want to pipe to a file (&lt;code&gt;iptables-save &amp;gt; output.txt&lt;/code&gt;) or a pager (&lt;code&gt;iptables-save | less&lt;/code&gt;) to more easily review the rules.&lt;/p&gt;

&lt;p&gt;To list just the Kubernetes Service NAT rules, use the &lt;code&gt;iptables&lt;/code&gt; command and the &lt;code&gt;-L&lt;/code&gt; flag to specify the correct chain:&lt;/p&gt;
&lt;pre class="code-pre super_user"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="#"&gt;iptables -t nat -L KUBE-SERVICES
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Chain KUBE-SERVICES (2 references)
target     prot opt source               destination
KUBE-SVC-TCOU7JCQXEZGVUNU  udp  --  anywhere             10.32.0.10           /* kube-system/kube-dns:dns cluster IP */ udp dpt:domain
KUBE-SVC-ERIFXISQEP7F7OF4  tcp  --  anywhere             10.32.0.10           /* kube-system/kube-dns:dns-tcp cluster IP */ tcp dpt:domain
KUBE-SVC-XGLOHA7QRQ3V22RZ  tcp  --  anywhere             10.32.226.209        /* kube-system/kubernetes-dashboard: cluster IP */ tcp dpt:https
. . .
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="querying-cluster-dns"&gt;Querying Cluster DNS&lt;/h2&gt;

&lt;p&gt;One way to debug your cluster DNS resolution is to deploy a debug container with all the tools you need, then use &lt;code&gt;kubectl&lt;/code&gt; to exec &lt;code&gt;nslookup&lt;/code&gt; on it. This is described in &lt;a href="https://kubernetes.io/docs/tasks/administer-cluster/dns-debugging-resolution/"&gt;the official Kubernetes documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Another way to query the cluster DNS is using &lt;code&gt;dig&lt;/code&gt; and &lt;code&gt;nsenter&lt;/code&gt; from a node. If &lt;code&gt;dig&lt;/code&gt; is not installed, it can be installed with &lt;code&gt;apt&lt;/code&gt; on Debian-based Linux distributions:&lt;/p&gt;
&lt;pre class="code-pre super_user"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="#"&gt;apt install dnsutils
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;First, find the cluster IP of the &lt;strong&gt;kube-dns&lt;/strong&gt; service:&lt;/p&gt;
&lt;pre class="code-pre super_user local-environment"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="#"&gt;kubectl get service -n kube-system kube-dns
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre  local-environment"&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;NAME       TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)         AGE
kube-dns   ClusterIP   &lt;span class="highlight"&gt;10.32.0.10&lt;/span&gt;   &amp;lt;none&amp;gt;        53/UDP,53/TCP   15d
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The cluster IP is highlighted above. Next we'll use &lt;code&gt;nsenter&lt;/code&gt; to run &lt;code&gt;dig&lt;/code&gt; in the a container namespace. Look at the section &lt;a href="#finding-and-entering-pod-network-namespaces"&gt;&lt;em&gt;Finding and Entering Pod Network Namespaces&lt;/em&gt;&lt;/a&gt; for more information on this:&lt;/p&gt;
&lt;pre class="code-pre super_user"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="#"&gt;nsenter -t &lt;span class="highlight"&gt;14346&lt;/span&gt; -n dig &lt;span class="highlight"&gt;kubernetes.default.svc.cluster.local&lt;/span&gt; @&lt;span class="highlight"&gt;10.32.0.10&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This &lt;code&gt;dig&lt;/code&gt; command looks up the Service's full domain name of &lt;strong&gt;&lt;span class="highlight"&gt;service-name&lt;/span&gt;.&lt;span class="highlight"&gt;namespace&lt;/span&gt;.svc.cluster.local&lt;/strong&gt; and specifics the IP of the cluster DNS service IP (&lt;code&gt;@&lt;span class="highlight"&gt;10.32.0.10&lt;/span&gt;&lt;/code&gt;).&lt;/p&gt;

&lt;h2 id="looking-at-ipvs-details"&gt;Looking at IPVS Details&lt;/h2&gt;

&lt;p&gt;As of Kubernetes 1.11, &lt;code&gt;kube-proxy&lt;/code&gt; can configure IPVS to handle the translation of virtual Service IPs to pod IPs. You can list the translation table of IPs with &lt;code&gt;ipvsadm&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre super_user"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="#"&gt;ipvsadm -Ln
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -&amp;gt; RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  100.64.0.1:443 rr
  -&amp;gt; 178.128.226.86:443           Masq    1      0          0
TCP  100.64.0.10:53 rr
  -&amp;gt; 100.96.1.3:53                Masq    1      0          0
  -&amp;gt; 100.96.1.4:53                Masq    1      0          0
UDP  100.64.0.10:53 rr
  -&amp;gt; 100.96.1.3:53                Masq    1      0          0
  -&amp;gt; 100.96.1.4:53                Masq    1      0          0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To show a single Service IP, use the &lt;code&gt;-t&lt;/code&gt; option and specify the desired IP:&lt;/p&gt;
&lt;pre class="code-pre super_user"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="#"&gt;ipvsadm -Ln -t &lt;span class="highlight"&gt;100.64.0.10:53&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Prot LocalAddress:Port Scheduler Flags
  -&amp;gt; RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  100.64.0.10:53 rr
  -&amp;gt; 100.96.1.3:53                Masq    1      0          0
  -&amp;gt; 100.96.1.4:53                Masq    1      0          0
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;In this article we’ve reviewed some commands and techniques for exploring and inspecting the details of your Kubernetes cluster's networking. For more information about Kubernetes, take a look at &lt;a href="https://www.digitalocean.com/community/tags/kubernetes?type=tutorials"&gt;our Kubernetes tutorials tag&lt;/a&gt; and &lt;a href="https://kubernetes.io/docs/home/"&gt;the official Kubernetes documentation&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/kubernetes-networking-under-the-hood</id>
    <published>2018-06-27T20:53:22Z</published>
    <updated>2018-09-19T19:18:49Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/kubernetes-networking-under-the-hood"/>
    <title>Kubernetes Networking Under the Hood</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;Kubernetes is a powerful container orchestration system that can manage the deployment and operation of containerized applications across clusters of servers. In addition to coordinating container workloads, Kubernetes provides the infrastructure and tools necessary to maintain reliable network connectivity between your applications and services.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://kubernetes.io/docs/concepts/cluster-administration/networking/"&gt;Kubernetes cluster networking documentation&lt;/a&gt; states that the basic requirements of a Kubernetes network are:&lt;/p&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;all containers can communicate with all other containers without NAT&lt;/li&gt;
&lt;li&gt;all nodes can communicate with all containers (and vice-versa) without NAT&lt;/li&gt;
&lt;li&gt;the IP that a container sees itself as is the same IP that others see it as&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;In this article we will discuss how Kubernetes satisfies these networking requirements within a cluster: how data moves inside a pod, between pods, and between nodes.&lt;/p&gt;

&lt;p&gt;We will also show how a Kubernetes &lt;strong&gt;Service&lt;/strong&gt; can provide a single static IP address and DNS entry for an application, easing communication with services that may be distributed among multiple constantly scaling and shifting pods.&lt;/p&gt;

&lt;p&gt;If you are unfamiliar with the terminology of Kubernetes &lt;em&gt;pods&lt;/em&gt; and &lt;em&gt;nodes&lt;/em&gt; or other basics, our article &lt;a href="https://www.digitalocean.com/community/tutorials/an-introduction-to-kubernetes"&gt;An Introduction to Kubernetes&lt;/a&gt; covers the general architecture and components involved.&lt;/p&gt;

&lt;p&gt;Let’s first take a look at the networking situation within a single pod.&lt;/p&gt;

&lt;h2 id="pod-networking"&gt;Pod Networking&lt;/h2&gt;

&lt;p&gt;In Kubernetes, a &lt;em&gt;pod&lt;/em&gt; is the most basic unit of organization: a group of tightly-coupled containers that are all closely related and perform a single function or service.&lt;/p&gt;

&lt;p&gt;Networking-wise, Kubernetes treats pods similar to a traditional virtual machine or a single bare-metal host: each pod receives a single unique IP address, and all containers within the pod share that address and communicate with each other over the &lt;strong&gt;lo&lt;/strong&gt; loopback interface using the &lt;strong&gt;localhost&lt;/strong&gt; hostname. This is achieved by assigning all of the pod's containers to the same network stack.&lt;/p&gt;

&lt;p&gt;This situation should feel familiar to anybody who has deployed multiple services on a single host before the days of containerization. All the services need to use a unique port to listen on, but otherwise communication is uncomplicated and has low overhead.&lt;/p&gt;

&lt;h2 id="pod-to-pod-networking"&gt;Pod to Pod Networking&lt;/h2&gt;

&lt;p&gt;Most Kubernetes clusters will need to deploy multiple pods per node. Pod to pod communication may happen between two pods on the same node, or between two different nodes.&lt;/p&gt;

&lt;h3 id="pod-to-pod-communication-on-one-node"&gt;Pod to Pod Communication on One Node&lt;/h3&gt;

&lt;p&gt;On a single node you can have multiple pods that need to communicate directly with each other. Before we trace the route of a packet between pods, let's inspect the networking setup of a node. The following diagram provides an overview, which we will walk through in detail:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/k8s-networking/single.png" alt="Networking overview of a single Kubernetes node"&gt;&lt;/p&gt;

&lt;p&gt;Each node has a network interface – &lt;strong&gt;eth0&lt;/strong&gt; in this example – attached to the Kubernetes cluster network. This interface sits within the node's &lt;strong&gt;root&lt;/strong&gt; network namespace. This is the default namespace for networking devices on Linux.&lt;/p&gt;

&lt;p&gt;Just as process namespaces enable containers to isolate running applications from each other, network namespaces isolate network devices such as interfaces and bridges. Each pod on a node is assigned its own isolated network namespace.&lt;/p&gt;

&lt;p&gt;Pod namespaces are connected back to the &lt;strong&gt;root&lt;/strong&gt; namespace with a &lt;em&gt;virtual ethernet pair&lt;/em&gt;, essentially a pipe between the two namespaces with an interface on each end (here we're using &lt;strong&gt;veth1&lt;/strong&gt; in the &lt;strong&gt;root&lt;/strong&gt; namespace, and &lt;strong&gt;eth0&lt;/strong&gt; within the pod).&lt;/p&gt;

&lt;p&gt;Finally, the pods are connected to each other and to the node's &lt;strong&gt;eth0&lt;/strong&gt; interface via a bridge, &lt;strong&gt;br0&lt;/strong&gt; (your node may use something like &lt;strong&gt;cbr0&lt;/strong&gt; or &lt;strong&gt;docker0&lt;/strong&gt;). A bridge essentially works like a physical ethernet switch, using either ARP (address resolution protocol) or IP-based routing to look up other local interfaces to direct traffic to.&lt;/p&gt;

&lt;p&gt;Let's trace a packet from &lt;strong&gt;pod1&lt;/strong&gt; to &lt;strong&gt;pod2&lt;/strong&gt; now:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;pod1&lt;/strong&gt; creates a packet with &lt;strong&gt;pod2&lt;/strong&gt;'s IP as its destination&lt;/li&gt;
&lt;li&gt;The packet travels over the virtual ethernet pair to the root network namespace&lt;/li&gt;
&lt;li&gt;The packet continues to the bridge &lt;strong&gt;br0&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Because the destination pod is on the same node, the bridge sends the packet to &lt;strong&gt;pod2&lt;/strong&gt;'s virtual ethernet pair&lt;/li&gt;
&lt;li&gt;the packet travels through the virtual ethernet pair, into &lt;strong&gt;pod2&lt;/strong&gt;'s network namespace and the pod's &lt;strong&gt;eth0&lt;/strong&gt; network interface&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now that we've traced a packet from pod to pod within a node, let's look at how pod traffic travels between nodes.&lt;/p&gt;

&lt;h3 id="pod-to-pod-communication-between-two-nodes"&gt;Pod to Pod Communication Between Two Nodes&lt;/h3&gt;

&lt;p&gt;Because each pod in a cluster has a unique IP, and every pod can communicate directly with all other pods, a packet moving between pods on two different nodes is very similar to the previous scenario.&lt;/p&gt;

&lt;p&gt;Let's trace a packet from &lt;strong&gt;pod1&lt;/strong&gt; to &lt;strong&gt;pod3&lt;/strong&gt;, which is on a different node:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/k8s-networking/double.png" alt="Networking diagram between two Kubernetes nodes"&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;pod1&lt;/strong&gt; creates a packet with &lt;strong&gt;pod3&lt;/strong&gt;'s IP as its destination&lt;/li&gt;
&lt;li&gt;The packet travels over the virtual ethernet pair to the root network namespace&lt;/li&gt;
&lt;li&gt;The packet continues to the bridge &lt;strong&gt;br0&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;The bridge finds no local interface to route to, so the packet is sent out the default route toward &lt;strong&gt;eth0&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Optional:&lt;/em&gt; if your cluster requires a network overlay to properly route packets to nodes, the packet may be encapsulated in a VXLAN packet (or other network virtualization technique) before heading to the network. Alternately, the network itself may be set up with the proper static routes, in which case the packet travels to eth0 and out the the network unaltered.&lt;/li&gt;
&lt;li&gt;The packet enters the cluster network and is routed to the correct node.&lt;/li&gt;
&lt;li&gt;The packet enters the destination node on &lt;strong&gt;eth0&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Optional:&lt;/em&gt; if your packet was encapsulated, it will be de-encapsulated at this point&lt;/li&gt;
&lt;li&gt;The packet continues to the bridge &lt;strong&gt;br0&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;The bridge routes the packet to the destination pod's virtual ethernet pair&lt;/li&gt;
&lt;li&gt;The packet passes through the virtual ethernet pair to the pod's &lt;strong&gt;eth0&lt;/strong&gt; interface&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now that we are familiar with how packets are routed via pod IP addresses, let's take a look at Kubernetes &lt;em&gt;services&lt;/em&gt; and how they build on top of this infrastructure.&lt;/p&gt;

&lt;h2 id="pod-to-service-networking"&gt;Pod to Service Networking&lt;/h2&gt;

&lt;p&gt;It would be difficult to send traffic to a particular application using just pod IPs, as the dynamic nature of a Kubernetes cluster means pods can be moved, restarted, upgraded, or scaled in and out of existence. Additionally, some services will have many replicas, so we need some way to load balance between them.&lt;/p&gt;

&lt;p&gt;Kubernetes solves this problem with &lt;em&gt;Services&lt;/em&gt;. A Service is an API object that maps a single virtual IP (VIP) to a set of pod IPs. Additionally, Kubernetes provides a DNS entry for each service's name and virtual IP, so services can be easily addressed by name.&lt;/p&gt;

&lt;p&gt;The mapping of virtual IPs to pod IPs within the cluster is coordinated by the &lt;code&gt;kube-proxy&lt;/code&gt; process on each node. This process sets up either &lt;a href="https://www.digitalocean.com/community/tutorials/a-deep-dive-into-iptables-and-netfilter-architecture"&gt;iptables&lt;/a&gt; or IPVS to automatically translate VIPs into pod IPs before sending the packet out to the cluster network. Individual connections are tracked so packets can be properly de-translated when they return. IPVS and iptables can both do load balancing of a single service virtual IP into multiple pod IPs, though IPVS has much more flexibility in the load balancing algorithms it can use.&lt;/p&gt;

&lt;p&gt;&lt;span class='note'&gt;&lt;strong&gt;Note:&lt;/strong&gt; this translation and connection tracking processes happens entirely in the Linux kernel. kube-proxy reads from the Kubernetes API and updates iptables ip IPVS, but it is not in the data path for individual packets. This is more efficient and higher performance than previous versions of kube-proxy, which functioned as a user-land proxy.&lt;br&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;Let's follow the route a packet takes from a pod, &lt;strong&gt;pod1&lt;/strong&gt; again, to a service, &lt;strong&gt;service1&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/k8s-networking/double-service.png" alt="Networking diagram between two Kubernetes nodes, showing DNAT translation of virtual IPs"&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;pod1&lt;/strong&gt; creates a packet with &lt;strong&gt;service1&lt;/strong&gt;'s IP as its destination&lt;/li&gt;
&lt;li&gt;The packet travels over the virtual ethernet pair to the root network namespace&lt;/li&gt;
&lt;li&gt;The packet continues to the bridge &lt;strong&gt;br0&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;The bridge finds no local interface to route the packet to, so the packet is sent out the default route toward &lt;strong&gt;eth0&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Iptables or IPVS, set up by &lt;code&gt;kube-proxy&lt;/code&gt;, match the packet's destination IP and translate it from a virtual IP to one of the service's pod IPs, using whichever load balancing algorithms are available or specified&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Optional:&lt;/em&gt; your packet may be encapsulated at this point, as discussed in the previous section&lt;/li&gt;
&lt;li&gt;The packet enters the cluster network and is routed to the correct node.&lt;/li&gt;
&lt;li&gt;The packet enters the destination node on &lt;strong&gt;eth0&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Optional:&lt;/em&gt; if your packet was encapsulated, it will be de-encapsulated at this point&lt;/li&gt;
&lt;li&gt;The packet continues to the bridge &lt;strong&gt;br0&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;The packet is sent to the virtual ethernet pair via &lt;strong&gt;veth1&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;The packet passes through the virtual ethernet pair and enters the pod network namespace via its &lt;strong&gt;eth0&lt;/strong&gt; network interface&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When the packet returns to &lt;strong&gt;node1&lt;/strong&gt; the VIP to pod IP translation will be reversed, and the packet will be back through the bridge and virtual interface to the correct pod.&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;In this article we’ve reviewed the internal networking infrastructure of a Kubernetes cluster. We’ve discussed the building blocks that make up the network, and detailed the hop-by-hop journey of packets in different scenarios.&lt;/p&gt;

&lt;p&gt;For more information about Kubernetes, take a look at &lt;a href="https://www.digitalocean.com/community/tags/kubernetes?type=tutorials"&gt;our Kubernetes tutorials tag&lt;/a&gt; and &lt;a href="https://kubernetes.io/docs/home/"&gt;the official Kubernetes documentation&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-install-nginx-on-ubuntu-18-04-quickstart</id>
    <published>2018-07-23T13:46:12Z</published>
    <updated>2018-07-23T13:56:16Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-install-nginx-on-ubuntu-18-04-quickstart"/>
    <title>How To Install Nginx on Ubuntu 18.04 [Quickstart]</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;Nginx is one of the most popular web servers in the world and is responsible for hosting some of the largest and highest-traffic sites on the internet. It is more resource-friendly than Apache in most cases and can be used as a web server or reverse proxy.&lt;/p&gt;

&lt;p&gt;In this guide, we'll explain how to install Nginx on your Ubuntu 18.04 server. For a more detailed version of this tutorial, please refer to &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-nginx-on-ubuntu-18-04"&gt;How To Install Nginx on Ubuntu 18.04&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;Before you begin this guide, you should have the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An Ubuntu 18.04 server and a regular, non-root user with sudo privileges. Additionally, you will need to enable a basic firewall to block non-essential ports. You can learn how to configure a regular user account and set up a firewall by following our &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-18-04"&gt;initial server setup guide for Ubuntu 18.04&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When you have an account available, log in as your non-root user to begin.&lt;/p&gt;

&lt;h2 id="step-1-–-installing-nginx"&gt;Step 1 – Installing Nginx&lt;/h2&gt;

&lt;p&gt;Because Nginx is available in Ubuntu's default repositories, you can install it using the &lt;code&gt;apt&lt;/code&gt; packaging system.&lt;/p&gt;

&lt;p&gt;Update your local package index:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt update
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Install Nginx: &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install nginx
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="step-2-–-adjusting-the-firewall"&gt;Step 2 – Adjusting the Firewall&lt;/h2&gt;

&lt;p&gt;Check the available &lt;code&gt;ufw&lt;/code&gt; application profiles: &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw app list
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Available applications:
  Nginx Full
  Nginx HTTP
  Nginx HTTPS
  OpenSSH
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let's enable the most restrictive profile that will still allow the traffic you've configured, permitting traffic on port &lt;code&gt;80&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw allow 'Nginx HTTP'
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Verify the change:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw status
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Status: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere                  
Nginx HTTP                 ALLOW       Anywhere                  
OpenSSH (v6)               ALLOW       Anywhere (v6)             
Nginx HTTP (v6)            ALLOW       Anywhere (v6)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="step-3-–-checking-your-web-server"&gt;Step 3 – Checking your Web Server&lt;/h2&gt;

&lt;p&gt;Check with the &lt;code&gt;systemd&lt;/code&gt; init system to make sure the service is running by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;systemctl status nginx
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;● nginx.service - A high performance web server and a reverse proxy server
   Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled)
   Active: &lt;span class="highlight"&gt;active (running)&lt;/span&gt; since Fri 2018-04-20 16:08:19 UTC; 3 days ago
     Docs: man:nginx(8)
 Main PID: 2369 (nginx)
    Tasks: 2 (limit: 1153)
   CGroup: /system.slice/nginx.service
           ├─2369 nginx: master process /usr/sbin/nginx -g daemon on; master_process on;
           └─2380 nginx: worker process
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Access the default Nginx landing page to confirm that the software is running properly through your IP address: &lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;http://&lt;span class="highlight"&gt;your_server_ip&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see the default Nginx landing page:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/nginx_1604/default_page.png" alt="Nginx default page"&gt;&lt;/p&gt;

&lt;h2 id="step-4-–-setting-up-server-blocks-recommended"&gt;Step 4 – Setting Up Server Blocks (Recommended)&lt;/h2&gt;

&lt;p&gt;When using the Nginx web server, you can use &lt;em&gt;server blocks&lt;/em&gt; (similar to virtual hosts in Apache) to encapsulate configuration details and host more than one domain from a single server. We will set up a domain called &lt;strong&gt;example.com&lt;/strong&gt;, but you should &lt;strong&gt;replace this with your own domain name&lt;/strong&gt;. To learn more about setting up a domain name with DigitalOcean, see our &lt;a href="https://www.digitalocean.com/community/tutorials/an-introduction-to-digitalocean-dns"&gt;introduction to DigitalOcean DNS&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Create the directory for &lt;code&gt;&lt;span class="highlight"&gt;example.com&lt;/span&gt;&lt;/code&gt;, using the &lt;code&gt;-p&lt;/code&gt; flag to create any necessary parent directories:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo mkdir -p /var/www/&lt;span class="highlight"&gt;example.com&lt;/span&gt;/html
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Assign ownership of the directory:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo chown -R $USER:$USER /var/www/&lt;span class="highlight"&gt;example.com&lt;/span&gt;/html
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The permissions of your web roots should be correct if you haven't modified your &lt;code&gt;umask&lt;/code&gt; value, but you can make sure by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo chmod -R 755 /var/www/&lt;span class="highlight"&gt;example.com&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Create a sample &lt;code&gt;index.html&lt;/code&gt; page using &lt;code&gt;nano&lt;/code&gt; or your favorite editor:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nano /var/www/&lt;span class="highlight"&gt;example.com&lt;/span&gt;/html/index.html
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Inside, add the following sample HTML:&lt;/p&gt;
&lt;div class="code-label " title="/var/www/example.com/html/index.html"&gt;/var/www/example.com/html/index.html&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&amp;lt;html&amp;gt;
    &amp;lt;head&amp;gt;
        &amp;lt;title&amp;gt;Welcome to &lt;span class="highlight"&gt;Example.com&lt;/span&gt;!&amp;lt;/title&amp;gt;
    &amp;lt;/head&amp;gt;
    &amp;lt;body&amp;gt;
        &amp;lt;h1&amp;gt;Success!  The &lt;span class="highlight"&gt;example.com&lt;/span&gt; server block is working!&amp;lt;/h1&amp;gt;
    &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save and close the file when you are finished.&lt;/p&gt;

&lt;p&gt;Make a new server block at &lt;code&gt;/etc/nginx/sites-available/&lt;span class="highlight"&gt;example.com&lt;/span&gt;&lt;/code&gt;:  &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/nginx/sites-available/&lt;span class="highlight"&gt;example.com&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Paste in the following configuration block, updated for our new directory and domain name:&lt;/p&gt;
&lt;div class="code-label " title="/etc/nginx/sites-available/example.com"&gt;/etc/nginx/sites-available/example.com&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;server {
        listen 80;
        listen [::]:80;

        root /var/www/&lt;span class="highlight"&gt;example.com&lt;/span&gt;/html;
        index index.html index.htm index.nginx-debian.html;

        server_name &lt;span class="highlight"&gt;example.com&lt;/span&gt; www.&lt;span class="highlight"&gt;example.com&lt;/span&gt;;

        location / {
                try_files $uri $uri/ =404;
        }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save and close the file when you are finished.&lt;/p&gt;

&lt;p&gt;Enable the file by creating a link from it to the &lt;code&gt;sites-enabled&lt;/code&gt; directory:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ln -s /etc/nginx/sites-available/&lt;span class="highlight"&gt;example.com&lt;/span&gt; /etc/nginx/sites-enabled/
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Two server blocks are now enabled and configured to respond to requests based on their &lt;code&gt;listen&lt;/code&gt; and &lt;code&gt;server_name&lt;/code&gt; directives:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;example.com&lt;/code&gt;: Will respond to requests for &lt;code&gt;example.com&lt;/code&gt; and &lt;code&gt;www.example.com&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;default&lt;/code&gt;: Will respond to any requests on port &lt;code&gt;80&lt;/code&gt; that do not match the other two blocks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To avoid a possible hash bucket memory problem that can arise from adding additional server names, it is necessary to adjust a single value in the &lt;code&gt;/etc/nginx/nginx.conf&lt;/code&gt; file.  Open the file:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/nginx/nginx.conf
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Find the &lt;code&gt;server_names_hash_bucket_size&lt;/code&gt; directive and remove the &lt;code&gt;#&lt;/code&gt; symbol to uncomment the line:&lt;/p&gt;
&lt;div class="code-label " title="/etc/nginx/nginx.conf"&gt;/etc/nginx/nginx.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;...
http {
    ...
    server_names_hash_bucket_size 64;
    ...
}
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Test for syntax errors:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nginx -t
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Restart Nginx to enable your changes:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl restart nginx
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nginx should now be serving your domain name. You can test this by navigating to &lt;code&gt;http://&lt;span class="highlight"&gt;example.com&lt;/span&gt;&lt;/code&gt;, where you should see something like this:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/nginx_server_block_1404/first_block.png" alt="Nginx first server block"&gt;&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Now that you have your web server installed, you have many options for the type of content to serve and the technologies you want to use to create a richer experience.&lt;/p&gt;

&lt;p&gt;If you'd like to build out a more complete application stack, check out this article on &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-linux-nginx-mysql-php-lemp-stack-ubuntu-18-04"&gt;how to configure a LEMP stack on Ubuntu 18.04&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
  <entry>
    <id>tag:www.digitalocean.com,2005:/community/tutorials/how-to-install-the-apache-web-server-on-ubuntu-18-04-quickstart</id>
    <published>2018-07-23T13:22:27Z</published>
    <updated>2018-07-23T13:30:02Z</updated>
    <link rel="alternate" type="text/html" href="https://www.digitalocean.com/community/tutorials/how-to-install-the-apache-web-server-on-ubuntu-18-04-quickstart"/>
    <title>How To Install the Apache Web Server on Ubuntu 18.04 [Quickstart]</title>
    <content type="html">&lt;h3 id="introduction"&gt;Introduction&lt;/h3&gt;

&lt;p&gt;The Apache HTTP server is the most widely-used web server in the world.  It provides many powerful features, including dynamically loadable modules, robust media support, and extensive integration with other popular software.&lt;/p&gt;

&lt;p&gt;In this guide, we'll explain how to install an Apache web server on your Ubuntu 18.04 server. For a more detailed version of this tutorial, please refer to &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-the-apache-web-server-on-ubuntu-18-04"&gt;How To Install the Apache Web Server on Ubuntu 18.04&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;Before you begin this guide, you should have the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An Ubuntu 18.04 server and a regular, non-root user with sudo privileges. Additionally, you will need to enable a basic firewall to block non-essential ports. You can learn how to configure a regular user account and set up a firewall for your server by following our &lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-18-04"&gt;initial server setup guide for Ubuntu 18.04&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When you have an account available, log in as your non-root user to begin.&lt;/p&gt;

&lt;h2 id="step-1-—-installing-apache"&gt;Step 1 — Installing Apache&lt;/h2&gt;

&lt;p&gt;Apache is available within Ubuntu's default software repositories, so you can install it using conventional package management tools.&lt;/p&gt;

&lt;p&gt;Update your local package index:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt update
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Install the &lt;code&gt;apache2&lt;/code&gt; package:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apt install apache2
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="step-2-—-adjusting-the-firewall"&gt;Step 2 — Adjusting the Firewall&lt;/h2&gt;

&lt;p&gt;Check the available &lt;code&gt;ufw&lt;/code&gt; application profiles: &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw app list
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Available applications:
  Apache
  Apache Full
  Apache Secure
  OpenSSH
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let's enable the most restrictive profile that will still allow the traffic you've configured, permitting traffic on port &lt;code&gt;80&lt;/code&gt; (normal, unencrypted web traffic):  &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw allow 'Apache'
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Verify the change:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo ufw status
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Status: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere                  
Apache                     ALLOW       Anywhere                  
OpenSSH (v6)               ALLOW       Anywhere (v6)             
Apache (v6)                ALLOW       Anywhere (v6)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="step-3-—-checking-your-web-server"&gt;Step 3 — Checking your Web Server&lt;/h2&gt;

&lt;p&gt;Check with the &lt;code&gt;systemd&lt;/code&gt; init system to make sure the service is running by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl status apache2
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;● apache2.service - The Apache HTTP Server
   Loaded: loaded (/lib/systemd/system/apache2.service; enabled; vendor preset: enabled)
  Drop-In: /lib/systemd/system/apache2.service.d
           └─apache2-systemd.conf
   Active: &lt;span class="highlight"&gt;active (running)&lt;/span&gt; since Tue 2018-04-24 20:14:39 UTC; 9min ago
 Main PID: 2583 (apache2)
    Tasks: 55 (limit: 1153)
   CGroup: /system.slice/apache2.service
           ├─2583 /usr/sbin/apache2 -k start
           ├─2585 /usr/sbin/apache2 -k start
           └─2586 /usr/sbin/apache2 -k start
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Access the default Apache landing page to confirm that the software is running properly through your IP address:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;http://&lt;span class="highlight"&gt;your_server_ip&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see the default Ubuntu 18.04 Apache web page:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/how-to-install-lamp-ubuntu-16/small_apache_default.png" alt="Apache default page"&gt;&lt;/p&gt;

&lt;h2 id="step-4-—-setting-up-virtual-hosts-recommended"&gt;Step 4 — Setting Up Virtual Hosts (Recommended)&lt;/h2&gt;

&lt;p&gt;When using the Apache web server, you can use &lt;em&gt;virtual hosts&lt;/em&gt; (similar to server blocks in Nginx) to encapsulate configuration details and host more than one domain from a single server. We will set up a domain called &lt;strong&gt;example.com&lt;/strong&gt;, but you should &lt;strong&gt;replace this with your own domain name&lt;/strong&gt;. To learn more about setting up a domain name with DigitalOcean, see our &lt;a href="https://www.digitalocean.com/community/tutorials/an-introduction-to-digitalocean-dns"&gt;introduction to DigitalOcean DNS&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Create the directory for &lt;code&gt;&lt;span class="highlight"&gt;example.com&lt;/span&gt;&lt;/code&gt;, using the &lt;code&gt;-p&lt;/code&gt; flag to create any necessary parent directories:&lt;/p&gt;
&lt;pre class="code-pre commmand"&gt;&lt;code langs=""&gt;sudo mkdir -p /var/www/&lt;span class="highlight"&gt;example.com&lt;/span&gt;/html
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Assign ownership of the directory:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo chown -R $USER:$USER /var/www/&lt;span class="highlight"&gt;example.com&lt;/span&gt;/html
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The permissions of your web roots should be correct if you haven't modified your &lt;code&gt;unmask&lt;/code&gt; value, but you can make sure by typing:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo chmod -R 755 /var/www/&lt;span class="highlight"&gt;example.com&lt;/span&gt;
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Create a sample &lt;code&gt;index.html&lt;/code&gt; page using &lt;code&gt;nano&lt;/code&gt; or your favorite editor:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;nano /var/www/&lt;span class="highlight"&gt;example.com&lt;/span&gt;/html/index.html
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Inside, add the following sample HTML:&lt;/p&gt;
&lt;div class="code-label " title="/var/www/example.com/html/index.html"&gt;/var/www/example.com/html/index.html&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&amp;lt;html&amp;gt;
    &amp;lt;head&amp;gt;
        &amp;lt;title&amp;gt;Welcome to &lt;span class="highlight"&gt;Example.com&lt;/span&gt;!&amp;lt;/title&amp;gt;
    &amp;lt;/head&amp;gt;
    &amp;lt;body&amp;gt;
        &amp;lt;h1&amp;gt;Success!  The &lt;span class="highlight"&gt;example.com&lt;/span&gt; server block is working!&amp;lt;/h1&amp;gt;
    &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save and close the file when you are finished.&lt;/p&gt;

&lt;p&gt;Make a new virtual host file at &lt;code&gt;/etc/apache2/sites-available/&lt;span class="highlight"&gt;example.com&lt;/span&gt;.conf&lt;/code&gt;: &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo nano /etc/apache2/sites-available/&lt;span class="highlight"&gt;example.com&lt;/span&gt;.conf
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Paste in the following configuration block, updated for our new directory and domain name:&lt;/p&gt;
&lt;div class="code-label " title="/etc/apache2/sites-available/example.com.conf"&gt;/etc/apache2/sites-available/example.com.conf&lt;/div&gt;&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&amp;lt;VirtualHost *:80&amp;gt;
    ServerAdmin &lt;span class="highlight"&gt;admin@example.com&lt;/span&gt;
    ServerName &lt;span class="highlight"&gt;example.com&lt;/span&gt;
    ServerAlias &lt;span class="highlight"&gt;www.example.com&lt;/span&gt;
    DocumentRoot /var/www/&lt;span class="highlight"&gt;example.com&lt;/span&gt;/html
    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined
&amp;lt;/VirtualHost&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save and close the file when you are finished.&lt;/p&gt;

&lt;p&gt;Enable the file with &lt;code&gt;a2ensite&lt;/code&gt;: &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo a2ensite &lt;span class="highlight"&gt;example.com&lt;/span&gt;.conf
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Disable the default site defined in &lt;code&gt;000-default.conf&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo a2dissite 000-default.conf
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Test for configuration errors: &lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo apache2ctl configtest
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see the following output:&lt;/p&gt;
&lt;pre class="code-pre "&gt;&lt;code langs=""&gt;&lt;div class="secondary-code-label " title="Output"&gt;Output&lt;/div&gt;Syntax OK
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Restart Apache to implement your changes:&lt;/p&gt;
&lt;pre class="code-pre command"&gt;&lt;code langs=""&gt;&lt;ul class="prefixed"&gt;&lt;li class="line" prefix="$"&gt;sudo systemctl restart apache2
&lt;/li&gt;&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Apache should now be serving your domain name. You can test this by navigating to &lt;code&gt;http://&lt;span class="highlight"&gt;example.com&lt;/span&gt;&lt;/code&gt;, where you should see something like this:&lt;/p&gt;

&lt;p&gt;&lt;img src="https://assets.digitalocean.com/articles/apache_virt_hosts_1404/example.png" alt="Apache virtual host example"&gt;&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Now that you have your web server installed, you have many options for the type of content to serve and the technologies you want to use to create a richer experience.&lt;/p&gt;

&lt;p&gt;If you'd like to build out a more complete application stack, check out this article on &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-linux-apache-mysql-php-lamp-stack-ubuntu-18-04"&gt;how to configure a LAMP stack on Ubuntu 18.04&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>DigitalOcean</name>
    </author>
  </entry>
</feed>
