One place for hosting & domains

      Use and Modify Official SaltStack Formulas


      Updated by Linode Contributed by Linode

      Salt State Files

      The SaltStack Platform is made up of two primary components: A remote execution engine which handles bi-directional communication for any node within your infrastructure (master and minions), and a configuration management system which maintains all infrastructure nodes in a defined state. Salt’s configuration management system is known as the Salt State system. A Salt state is declared within a Salt State file (SLS) using YAML syntax and represents the information Salt needs to configure minions. A Salt Formula is a collection of related SLS files that will achieve a common configuration.

      SaltStack’s GitHub page contains Salt formulas for commonly needed configurations, like creating and managing SSL/TLS certificates, installing and configuring the Apache HTTP Server, installing and configuring a WordPress site and many other useful formulas. You can easily add any of these pre-written formulas to your own Salt state tree using GitHub.

      This guide will use GitHub to fork and modify SaltStack’s timezone formula and then use the formula on a Salt master to configure the time zone on two minions.

      Before You Begin

      1. If you are new to SaltStack, read A Beginner’s Guide to Salt to familiarize yourself with basic Salt concepts.

      2. Download Git on your local computer by following our How to Install Git on Linux, Mac or Windows guide.

      3. Familiarize yourself with Git using our Getting Started with Git guide.

      4. Make sure you have configured git on your local computer.

      5. Use the Getting Started with Salt – Basic Installation and Setup guide to set up a Salt Master and two Salt minions: one running Ubuntu 18.04 and the second running CentOS 7.

      6. Complete the sections of our Securing Your Server to create a standard user account, harden SSH access and remove unnecessary network services.

      Note

      The steps in this guide require root privileges. Be sure to run the steps below with the sudo prefix. For more information on privileges, see our Users and Groups guide.

      Overview of the SaltStack Time Zone Formula

      In this section, we will take a closer look at SaltStack’s timezone-formula, which can be used to configure a minion’s time zone. A high-level overview of all the formula’s state files and Jinja templates will be provided. Salt best practices recommends that formulas should separate the data that a state needs from the state itself to increase the flexibility and reusability of state files. We will observe how this is achieved in the time zone formula.

      1. In your browser, navigate to the timezone-formula on SaltStack’s GitHub page. The README file is displayed and contains basic information about the formula. It notes the following details:

        • The purpose of the formula: to configure the time zone.
        • The available states: timezone
        • The provided default values: timezone: 'Europe/Berlin' utc: True

        The repository’s FORMULA file includes additional details, including the supported OS families (Debian, RedHat, SUSE, Arch, FreeBSD), a summary, description and release number.

      2. Viewing the timezone-formula, click on the timezone directory to view its contents. You should see the following files:

        View the time zone formula files

      3. Take a look at the contents of the init.sls file that defines the timezone state:

        timezone/init.sls
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        
        # This state configures the timezone.
        
        {%- set timezone = salt['pillar.get']('timezone:name', 'Europe/Berlin') %}
        {%- set utc = salt['pillar.get']('timezone:utc', True) %}
        {% from "timezone/map.jinja" import confmap with context %}
        
        timezone_setting:
          timezone.system:
            - name: {{ timezone }}
            - utc: {{ utc }}
        
        timezone_packages:
          pkg.installed:
            - name: {{ confmap.pkgname }}
        
        timezone_symlink:
          file.symlink:
            - name: {{ confmap.path_localtime }}
            - target: {{ confmap.path_zoneinfo }}{{ timezone }}
            - force: true
            - require:
              - pkg: {{ confmap.pkgname }}
            

        Salt will interpret the name of this file as timezone, since any init.sls file in a subdirectory is referred to by the path of the directory.

        This state file contains three state declarations, timezone_setting, timezone_packages and timezone_symlink. Below is a description of the configuration each declaration will accomplish on a Salt minion.

        • timezone.system: This state uses Salt’s timezone state module to manage the timezone for the minion. The values for name and utc are derived from the corresponding Salt master’s Pillar file. This is accomplished in the two variable assignment at the top of the file: {%- set timezone = salt['pillar.get']('timezone:name', 'Europe/Berlin') %} and {%- set utc = salt['pillar.get']('timezone:utc', True) %}.

        • timezone_packages: This state ensures that the package needed to configure time zones is installed on the minion. This value is derived from the confmap variable that is imported from the map.jinja file. The import is declared at the top of the file with the {% from "timezone/map.jinja" import confmap with context %} import statement. Later in this section, you will inspect the map.jinja file.

        • timezone_symlink: This state creates a symbolic link from the path defined in name to the location defined in target. This state will only execute if the timezone_packages state is executed successfully. This requirement is denoted by the require statement.

      4. Next, inspect the map.jinja file:

        timezone/map.jinja
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
        15
        16
        
        {% import_yaml "timezone/defaults.yaml" as defaults %}
        {% import_yaml "timezone/osfamilymap.yaml" as osfamilymap %}
        
        {% set osfam = salt['grains.filter_by'](
                                                osfamilymap,
                                                grain='os_family'
                                                ) or {} %}
        
        {% do salt['defaults.merge'](defaults, osfam) %}
        
        {%- set confmap = salt['pillar.get'](
                                        'timezone:lookup',
                                        default=defaults,
                                        merge=True,
                                        ) %}
              

        The map.jinja file allows the formula to abstract static defaults into a dictionary that contains platform specific data. The two main dictionaries are defined in the repository’s timezone/defaults.yaml and timezone/osfamilymap.yaml files. The defaults.yml file serves as a base dictionary containing values shared by all OSes, while the osfamilymap.yml file stores any values that are different from the base values. Any file throughout the formula could make use of these dictionary values by importing the map.jinja file. In addition, any dictionary values can be overridden in a Pillar file. Overidding dictionary values will be discussed in the Modify Your SaltStack Formula section.

      5. Open the timezone/defaults.yaml file and the timezone/osfamilymap,yaml file to view the data stored in those files:

        timezone/defaults.yaml
        1
        2
        3
        4
        
            path_localtime: /etc/localtime
            path_zoneinfo: /usr/share/zoneinfo/
            pkgname: tzdata
              
        timezone/osfamilymap.yaml
        1
        2
        3
        4
        5
        6
        7
        
            Suse:
              pkgname: timezone
            FreeBSD:
              pkgname: zoneinfo
            Gentoo:
              pkgname: sys-libs/timezone-data
              

        The values defined in these YAML files are used in the init.sls file.

      6. Open the pillar.example file to review its contents:

        pillar.example
        1
        2
        3
        4
        
          timezone:
            name: 'Europe/Berlin'
            utc: True
              

        This file provides an example for you to use when creating your own Pillar file on the Salt master. The init.sls file uses the values for name and utc in its timezone_setting state declaration. The value for name will set the time zone for your minion. The boolean value for utc determines whether or not to set the minion’s hardware clock to UTC.

        Refer to tz database time zones to view a list of all available time zones. Since Pillar files contain sensitive data, you should not version control this file. In the Create the Pillar section, you will create a Pillar file directly on your Salt master.

        Now that you understand the structure of the SaltStack time zone formula, in the next section you will fork the formula’s repository on GitHub and clone the forked formula to your local computer.

      Fork and Clone the SaltStack TimeZone Formula

      In this section you will fork the timezone-formula from the official SaltStack GitHub page to your GitHub account and clone it to a local repository.

      1. In your browser, navigate to the timezone-formula on SaltStack’s GitHub page. If you have not yet logged into your GitHub account, click on the Sign in link at the top of the page and log in.

      2. Fork the timezone-formula from the SaltStack formula’s GitHub page:

        Fork SaltStack timezone formula

        Once the formula has been forked, you will be redirected to your GitHub account’s own fork of the timezone formula.

      3. Viewing your fork of the timezone formula, click on the Clone or download button and copy the URL:

        Fork SaltStack timezone formula

      4. On your local computer, clone the timezone formula:

        git clone https://github.com/my-github/timezone-formula.git
        
      5. Move into the timezone-formula directory:

        cd timezone-formula
        
      6. Display the contents of the timezone-formula directory:

        ls
        

        You should see the following output:

          
              FORMULA        README.rst     pillar.example timezone
              
        
      7. When you clone a repository, Git will automatically set the origin remote to the location of the forked repository. Verify the configured remotes for your timezone-formula repository:

        git remote -v
        

        Your output should be similar to what is displayed below, however, it will point to your own fork of the timezone-formula repository:

          
          origin	https://github.com/my-github/timezone-formula.git (fetch)
          origin	https://github.com/my-github/timezone-formula.git (push)
              
        

        You can add the official SaltStack timezone formula as the upstream remote, so you can easily pull any changes made to the formula by the repository’s maintainers or contribute back to the project. This step is not required.

        git remote add upstream https://github.com/saltstack-formulas/timezone-formula
        

        You now have a local copy of your forked timezone-formula. In the next section, you will modify the formula to update the init.sls file.

      Modify Your SaltStack Formula

      In this section, you will modify the time zone formula to improve how the formula follows Salt best practices related to lookup dictionaries. You can similarly modify any SaltStack formula for your infrastructure’s specific requirements, if needed.

      As discussed in the Overview of the SaltStack Time Zone Formula section, the timezone/defaults.yaml file and the timezone/osfamily.map file provide dictionaries of values that are used by the init.sls state. These YAML file dictionary values can be overridden in a Pillar file that also stores any sensitive data needed by the init.sls state.

      When structuring Pillar data, Salt’s official documentation states that it is a best practice to make formulas expect all formula-related parameters to be placed under a second-level lookup key. Currently, the init.sls file’s timezone and utc variables expect the Pillar data to be structured differently. You will update these two variable statements to expect a second-level lookup key.

      1. Create a new branch in your local repository to begin modifying the timezone-formula:

        git checkout -b update-variable-statements
        
      2. Open the init.sls file in a text editor and modify its timezone and utc variable statements to match the example file:

        timezone/init.sls
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        
        # This state configures the timezone.
        
        {%- set timezone = salt['pillar.get']('timezone:lookup:name', 'Europe/Berlin') %}
        {%- set utc = salt['pillar.get']('timezone:lookup:utc', True) %}
        {% from "timezone/map.jinja" import confmap with context %}
        
        timezone_setting:
          timezone.system:
            - name: {{ timezone }}
            - utc: {{ utc }}
        
        timezone_packages:
          pkg.installed:
            - name: {{ confmap.pkgname }}
        
        timezone_symlink:
          file.symlink:
            - name: {{ confmap.path_localtime }}
            - target: {{ confmap.path_zoneinfo }}{{ timezone }}
            - force: true
            - require:
              - pkg: {{ confmap.pkgname }}
              

        The init.sls file now expects a second-level lookup key when retrieving the specified Pillar values. Following this convention will make it easier to override dictionary values in your Pillar file. You will create a Pillar file in the Installing a Salt Formula section of this guide.

      3. Use Git to view which files have been changed before staging them:

        git status
        

        Your output should resemble the following:

          
        On branch update-variable-statements
        Changes not staged for commit:
          &nbsp&nbsp(use "git add &ltfile&gt..." to update what will be committed)
          &nbsp&nbsp(use "git checkout -- &ltfile&gt..." to discard changes in working directory)
        
          &nbsp&nbspmodified:   timezone/init.sls
        
        no changes added to commit (use "git add" and/or "git commit -a")
            
        
      4. Stage and commit the changes you made to the init.sls file.

        git add -A
        git commit -m 'My commit message'
        
      5. Push your changes to your fork:

        git push origin update-variable-statements
        
      6. Navigate to your timezone formula’s remote GitHub repository and create a pull request against your fork’s master branch.

        Submit a pull request

        Make sure you select your own fork of the time zone formula as the base fork, otherwise you will submit a pull request against the official SaltStack timezone formula’s repository, which is not the intended behavior for this example.

        Create a pull request

        If you are satisfied with the changes in the pull request, merge the pull request into your master branch.

        Merge a pull request

      In the next section, you will add your forked timezone-formula to your Salt master, create a Pillar file for the timezone-formula and apply the changes to your minions.

      Install a Salt Formula

      There are two ways to use a Salt Formula: you can add the formula as a GitFS Remote, which will allow you to directly serve the files hosted on your GitHub account, or you can add the formula directly to the Salt master using Git’s clone mechanism. This section will cover both ways to use Salt formulas.

      Manually Add a Salt Formula to your Master

      1. Navigate to your fork of the timezone-formula, click on the Clone or download button and copy the repository’s URL to your clipboard.

      2. SSH into your Salt master. Replace the username with your limited user account and replace 198.51.100.0 with your Linode’s IP address:

        ssh username@198.51.100.0
        
      3. Create a formulas directory and go to the new directory:

        mkdir -p /srv/formulas
        cd /srv/formulas
        
      4. If your Salt master does not already have Git installed, install Git using your system’s package manager:

        Ubuntu/Debian

        apt-get update
        apt-get install git
        

        Centos

        yum update
        yum install git
        
      5. Clone the repository into the /srv/formulas directory. Make sure you replace git-username with your own username:

        git clone https://github.com/git-username/timezone-formula.git
        

      Add a Salt Formula as a GitFS Remote

      GitFs allows Salt to serve files directly from remote git repositories. This is a convenient way to use Salt formulas with the added flexibility and power that remote version control systems provide, like collaboration and easy rollback to previous versions of your formulas.

      1. On the Salt master, install the Python interface to Git:

        sudo apt-get install python-git
        
      2. Edit the Salt master configuration file to use GitFs as a fileserver backend. Make sure the lines listed below are uncommented in your master configuration file:

        /etc/salt/master
        1
        2
        3
        
        fileserver_backend:
          - gitfs
          - roots

        When using multiple backends, you should list all backends in the order you want them to be searched. roots is the fileserver backend used to serve files from any of the master’s directories listed in the file_roots configuration.

      3. In the same Salt master configuration file, add the location of your timezone formula’s GitHub repository. Ensure you have uncommented gitfs_remote:

        /etc/salt/master
        1
        2
        
        gitfs_remotes:
          - https://github.com/git-username/timezone-formula.git
      4. Uncomment the gitfs_provider declaration and set its value to gitpython:

        /etc/salt/master
        1
        
        gitfs_provider: gitpython
      5. Restart the Salt master to apply the new configurations:

        sudo systemctl restart salt-master
        

      Add a Salt Formula to the Top File

      To include your timezone formula in your Salt state tree, you must add it to your top file.

      1. Create the /srv/salt directory if it does not already exist:

        mkdir /srv/salt
        
      2. Add the timezone state declared in the timezone-formula to your top file:

        /srv/salt/top.sls
        1
        2
        3
        4
        
        base:
          '*':
            - timezone
            

        The example Top file declares one environment, the base environment that targets all minions and applies the timezone state to them. This top file could easily contain several states that already exist in your state tree, like an apache state, a wordpress state, etc., and several environments that target different minions. Any Salt formula can be easily dropped-in to the top file and will be applied to the targeted minions the next time you run a highstate.

      Create the Pillar

      1. Create a directory to store your formula’s Pillar file:

        mkdir -p /srv/pillar
        
      2. Create a Pillar file to store the data that will be used by your timezone formula:

        /srv/pillar/timezone.sls
        1
        2
        3
        4
        5
        6
        7
        8
        9
        
        timezone:
          lookup:
            {%- if grains['os_family'] == 'Debian' %}
            name: America/New_York
            {%- else %}
            name: 'Europe/Berlin'
            {%- endif %}
            utc: True
            

        The timezone.sls Pillar file was created from the pillar.example file provided in the SaltStack timezone formula. The example was modified to add Jinja control statements that will assign a different timezone on any minion that is a Debian family OS. You can replace any of the timezone name values to your preferred timezone or add additional Jinja logic, if necessary. For an introduction to Jinja, read the Introduction to Jinja Templates for Salt.

        You can also override any of the dictionary values defined in the timezone/defaults.yaml or timezone/osfamilymap.yaml in the Pillar file using Salt’s lookup dictionary convention. For example, if you wanted to override the pkgname value defined in timezone/defaults.yaml your Pillar file might look like the following example:

        /srv/pillar/timezone.sls
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        
        timezone:
          lookup:
            {%- if grains['os_family'] == 'Debian' %}
            name: America/New_York
            {%- else %}
            name: 'Europe/Berlin'
            {%- endif %}
            utc: True
            pkgname: timezone
            
      3. If you cloned the timezone-formula to your master instead of adding the formula as a GitFS remote, add the timezone-formula’s directory to the Salt master’s file_roots configuration:

        /etc/salt/master
        1
        2
        3
        4
        5
        
        file_roots:
          base:
            - /srv/salt/
            - /srv/formulas/timezone-formula
            
      4. Add the Pillar to the Pillar’s top file:

        /srv/pillar/top.sls
        1
        2
        3
        4
        
        base:
          '*':
            - timezone
            
      5. Configure the location of the Pillar file:

        /etc/salt/master
        1
        2
        3
        4
        
        pillar_roots:
          base:
            - /srv/pillar
            
      6. Restart the Salt master for the new configurations to take effect on the Salt master:

        sudo systemctl restart salt-master
        
      7. Run a highstate to your minion to apply the state defined in the timezone formula:

        sudo salt '*' state.apply
        

      Next Steps

      To learn how to create your own Salt formulas and how to organize your formula’s states in a logical and modular way, read our Automate Static Site Deployments with Salt, Git, and Webhooks guide.

      More Information

      You may wish to consult the following resources for additional information on this topic. While these are provided in the hope that they will be useful, please note that we cannot vouch for the accuracy or timeliness of externally hosted materials.

      Find answers, ask questions, and help others.

      This guide is published under a CC BY-ND 4.0 license.



      Source link

      Install Plex Media Server on Ubuntu 18.04 Using Salt Masterless


      Updated by Linode Contributed by Linode

      Plex is a media server that allows you to stream video and audio content that you own to many different types of devices. In this guide you will learn how to use a masterless Salt minion to set up a Plex server, attach and use a Block Storage Volume, and how to connect to your media server to stream content to your devices.

      Before You Begin

      1. Familiarize yourself with our Getting Started guide and complete the steps for setting your Linode’s hostname and timezone.

      2. Follow the steps in the How to Secure Your Server guide.

      3. Update your system:

        sudo apt-get update && sudo apt-get upgrade
        
      4. You will need to create a Block Storage Volume and attach it to your Linode. You will format and mount the drive as part of this guide. This volume will be used to store your media, so you should pick a size that’s appropriate for your media collection, though you can resize the volume later if you need more storage. For more on Block Storage, see our How to Use Block Storage guide.

      5. Plex requires an account to use their service. Visit the Plex website to sign up for an account if you do not already have one.

      Note

      The steps in this guide require root privileges. Be sure to run the steps below with the sudo prefix. For more information on privileges, see our Users and Groups guide.

      Prepare the Salt Minion

      1. On your Linode, create the /srv/salt and /srv/pillar directories. These are where the Salt state files and Pillar files will be housed.

        mkdir /srv/salt && mkdir /srv/pillar
        
      2. Install salt-minion via the Salt bootstrap script:

        curl -L https://bootstrap.saltstack.com -o bootstrap_salt.sh
        sudo sh bootstrap_salt.sh
        
      3. The Salt minion will use the official Plex Salt Formula, which is hosted on the SaltStack GitHub repository. In order to use a Salt formula hosted on an external repository, you will need GitPython installed. Install GitPython:

        sudo apt-get install python-git
        

      Modify the Salt Minion Configuration

      1. Because the Salt minion is running in masterless mode, you will need to modify the minion configuration file (/etc/salt/minion) to instruct Salt to look for state files locally. Open the minion configuration file in a text editor, uncomment the line #file_client: remote, and set it to local:

        /etc/salt/minion
        1
        2
        3
        4
        5
        6
        7
        8
        9
        
        ...
        
        # Set the file client. The client defaults to looking on the master server for
        # files, but can be directed to look at the local file directory setting
        # defined below by setting it to "local". Setting a local file_client runs the
        # minion in masterless mode.
        file_client: local
        
        ...
      2. There are some configuration values that do not normally exist in /etc/salt/minion which you will need to add in order to run your minion in masterless mode. Copy the following lines into the end of /etc/salt/minion:

        /etc/salt/minion
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        
        ...
        
        fileserver_backend:
          - roots
          - gitfs
        
        gitfs_remotes:
          - https://github.com/saltstack-formulas/plex-formula.git
        
        gitfs_provider: gitpython

        The fileserver_backend block instructs the Salt minion to look for Salt configuration files in two places. First, it tells Salt to look for Salt state files in our minion’s roots backend (/srv/salt). Secondly, it instructs Salt to use the Git Fileserver (gitfs) to look for Salt configuration files in any Git remote repositories that have been named in the gitfs_remotes section. The address for the Plex Salt formula’s Git repository is included in the gitfs_remotes section.

        Note

        It is best practice to create a fork of the Plex formula’s Git repository on GitHub and to add your fork’s Git repository address in the gitfs_remotes section. This will ensure that any further changes to the upstream Plex formula which might break your current configuration can be reviewed and handled accordingly, before applying them.

        Lastly, GitPython is specified as the gitfs_provider.

      Create the Salt State Tree

      1. Create a Salt state top file at /srv/salt/top.sls and copy in the following configuration. This file tells Salt to look for state files in the plex folder of the Plex formula’s Git repository, and for a state files named disk.sls and directory.sls, which you will create in the next steps.

        /srv/salt/top.sls
        1
        2
        3
        4
        5
        
        base:
          '*':
            - plex
            - disk
            - directory
      2. Create the disk.sls file in /srv/salt:

        /srv/salt/disk.sls
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        
        disk.format:
          module.run:
            - device: /dev/disk/by-id/scsi-0Linode_Volume_{{ pillar['volume_name'] }}
            - fs_type: ext4
        
        /mnt/plex:
          mount.mounted:
            - device: /dev/disk/by-id/scsi-0Linode_Volume_{{ pillar['volume_name'] }}
            - fstype: ext4
            - mkmnt: True
            - persist: True

        This file instructs Salt to prepare your Block Storage Volume for use with Plex. It first formats your Block Storage Volume with the ext4 filesystem type by using the disk.format Salt module, which can be run in a state file using module.run. Then disk.sls instructs Salt to mount your volume at /mnt/plex, creating the mount target if it does not already exist with mkmnt, and persisting the mount to /etc/fstab so that the volume is always mounted at boot.

      3. Create the directory.sls file in /srv/salt:

        /srv/salt/directory.sls
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        
        /mnt/plex/plex-media:
          file.directory:
            - require:
              - mount: /mnt/plex
            - user: username
            - group: plex
        
        /mnt/plex/plex-media/movies:
          file.directory:
            - require:
              - mount: /mnt/plex
            - user: username
            - group: plex
        
        /mnt/plex/plex-media/television:
          file.directory:
            - require:
              - mount: /mnt/plex
            - user: username
            - group: plex

        The directories that are created during this step are for organizational purposes, and will house your media. Make sure you replace username with the name of the limited user account you created when following the How to Secure Your Server guide. The location of the directories is the volume you mounted in the previous step. If you wish to add more directories, perhaps one for your music media, you can do so here, just be sure to include the - require block, as this prevents Salt from trying to create the directory before the Block Storage Volume has been mounted.

      4. Go to the Plex Media Server download page and note the most recent version of their Linux distribution. At the time of writing, the most recent version is 1.13.9.5456-ecd600442. Create the plex.sls Pillar file in /srv/pillar and change the Plex version number and the name of your Block Storage Volume as necessary:

        /srv/pillar/plex.sls
        1
        2
        3
        
        plex:
          version: 1.13.9.5456-ecd600442
        volume_name: plex
      5. Create the Salt Pillar top file in /srv/pillar. This file will instruct Salt to look for the plex.sls Pillar file you created in the previous step.

        /srv/pillar/top.sls
      6. Apply your Salt state locally using salt-call:

        salt-call --local state.apply
        

        You should see a list of the changes Salt applied to your system. You have successfully installed Plex using Salt.

      Set Up Plex

      Initial Configuration

      1. You’ll need to create an SSH tunnel to your Linode to connect to Plex’s web UI. On your local computer, run the following command, replacing <your_ip_address> with your Plex server’s IP address.:

        ssh username@<your_ip_address> -L 8888:localhost:32400
        
      2. In a browser, navigate to http://localhost:8888/web/.

      3. Sign in with your Plex username and password.

      4. Name your media server. This example uses the name linode-plex. Be sure to check the box that reads Allow me to access my media outside my home and then click Next.

        Name your media server

      Organize Your Media

      1. Click on the Add Library button:

        Click on Add Media

      2. Select Movies and click Next:

        Select Movies and click next

      3. Click Browse for Media Folder and select the appropriate folder at /mnt/plex/plex-media/movies. Then click Add:

        Select the appropriate folder

      4. Repeat the process to add your ‘Television’ folder.

      5. When you are done adding your libraries, click Add Library.

      6. To continue the configuration process, click Next.

      7. Click on Get Plex Apps to download the appropriate Plex client for your device. Then click Done.

        Download the appropriate client for your device

      8. In the future you can add more libraries by hovering over the menu and clicking the plus sign (+) next to LIBRARIES.

        Add more libraries

      DLNA is a protocol that incorporates Universal Plug and Play (or UPnP) standards for digital media sharing across devices. If you do not wish to make use of it, it’s recommended that you disable this feature, as it is openly connectable on port 1900. From the Plex web interface, click the wrench icon in the upper right corner, and navigate to the DLNA section under SETTINGS. Uncheck Enable the DLNA server, and click Save Changes:

      Disable DLNA

      Connect to Your Plex Server

      1. Visit the Plex Apps download page or the app store on your device to download Plex Media Player if you have not already done so.

      2. Open your Plex app. The example provided here will use the Plex Media Player for macOS.

      3. Sign in to Plex.

      4. On the left there’s a dropdown menu where you can select your server by the name you chose. Select your server.

        Connect to your Plex Server

      5. You are now able to stream your content with Plex.

        Plex's macOS App

      1. You can use SCP to transfer media to your server from your local computer. Replace your username and 123.456.7.8 with the IP address of your Linode.

        scp example_video.mp4 username@123.456.7.8:/mnt/plex/plex-media/movies
        
      2. Once you’ve transferred files to your Plex media server, you may need to scan for new files before they show up in your Library. Click on the ellipsis next to a Library and select Scan Library Files.

        Scan your Library for new files

      More Information

      You may wish to consult the following resources for additional information on this topic. While these are provided in the hope that they will be useful, please note that we cannot vouch for the accuracy or timeliness of externally hosted materials.

      Find answers, ask questions, and help others.

      This guide is published under a CC BY-ND 4.0 license.



      Source link

      How To Build a Node.js Application with Docker


      Introduction

      The Docker platform allows developers to package and run applications as containers. A container is an isolated process that runs on a shared operating system, offering a lighter weight alternative to virtual machines. Though containers are not new, they offer benefits — including process isolation and environment standardization — that are growing in importance as more developers use distributed application architectures.

      When building and scaling an application with Docker, the starting point is typically creating an image for your application, which you can then run in a container. The image includes your application code, libraries, configuration files, environment variables, and runtime. Using an image ensures that the environment in your container is standardized and contains only what is necessary to build and run your application.

      In this tutorial, you will create an application image for a static website that uses the Express framework and Bootstrap. You will then build a container using that image and push it to Docker Hub for future use. Finally, you will pull the stored image from your Docker Hub repository and build another container, demonstrating how you can recreate and scale your application.

      Prerequisites

      To follow this tutorial, you will need:

      Step 1 — Installing Your Application Dependencies

      To create your image, you will first need to make your application files, which you can then copy to your container. These files will include your application’s static content, code, and dependencies.

      First, create a directory for your project in your non-root user’s home directory. We will call ours node_project, but you should feel free to replace this with something else:

      Navigate to this directory:

      This will be the root directory of the project.

      Next, create a package.json file with your project's dependencies and other identifying information. Open the file with nano or your favorite editor:

      Add the following information about the project, including its name, author, license, entrypoint, and dependencies. Be sure to replace the author information with your own name and contact details:

      ~/node_project/package.json

      {
        "name": "nodejs-image-demo",
        "version": "1.0.0",
        "description": "nodejs image demo",
        "author": "Sammy the Shark <sammy@example.com>",
        "license": "MIT",
        "main": "app.js",
        "scripts": {
          "start": "node app.js",
          "test": "echo "Error: no test specified" && exit 1"
        },
        "keywords": [
          "nodejs",
          "bootstrap",
          "express"
        ],
        "dependencies": {
          "express": "^4.16.4"
        }
      }
      

      This file includes the project name, author, and license under which it is being shared. Npm recommends making your project name short and descriptive, and avoiding duplicates in the npm registry. We've listed the MIT license in the license field, permitting the free use and distribution of the application code.

      Additionally, the file specifies:

      • "main": The entrypoint for the application, app.js. You will create this file next.
      • "scripts": The commands that will run when you use npm start to start your application.
      • "dependencies": The project dependencies — in this case, Express 4.16.4 or above.

      Though this file does not list a repository, you can add one by following these guidelines on adding a repository to your package.json file. This is a good addition if you are versioning your application.

      Save and close the file when you've finished making changes.

      To install your project's dependencies, run the following command:

      This will install the packages you've listed in your package.json file in your project directory.

      We can now move on to building the application files.

      Step 2 — Creating the Application Files

      We will create a website that offers users information about sharks. Our application will have a main entrypoint, app.js, and a views directory that will include the project's static assets. The landing page, index.html, will offer users some preliminary information and a link to a page with more detailed shark information, sharks.html. In the views directory, we will create both the landing page and sharks.html.

      First, open app.js in the main project directory to define the project's routes:

      The first part of the file will create the Express application and Router objects, and define the base directory, port, and host as variables:

      ~/node_project/app.js

      var express = require("express");
      var app = express();
      var router = express.Router();
      
      var path = __dirname + '/views/';
      const PORT = 8080;
      const HOST = '0.0.0.0';
      

      The require function loads the express module, which we then use to create the app and router objects. The router object will perform the routing function of the application, and as we define HTTP method routes we will add them to this object to define how our application will handle requests.

      This section of the file also sets a few variables, path, PORT, and HOST:

      • path: Defines the base directory, which will be the views subdirectory within the current project directory.
      • HOST: Defines the address that the application will bind to and listen on. Setting this to 0.0.0.0 or all IPv4 addresses corresponds with Docker's default behavior of exposing containers to 0.0.0.0 unless otherwise instructed.
      • PORT: Tells the app to listen on and bind to port 8080.

      Next, set the routes for the application using the router object:

      ~/node_project/app.js

      ...
      
      router.use(function (req,res,next) {
        console.log("/" + req.method);
        next();
      });
      
      router.get("/",function(req,res){
        res.sendFile(path + "index.html");
      });
      
      router.get("/sharks",function(req,res){
        res.sendFile(path + "sharks.html");
      });
      

      The router.use function loads a middleware function that will log the router's requests and pass them on to the application's routes. These are defined in the subsequent functions, which specify that a GET request to the base project URL should return the index.html page, while a GET request to the /sharks route should return sharks.html.

      Finally, mount the router middleware and the application's static assets and tell the app to listen on port 8080:

      ~/node_project/app.js

      ...
      
      app.use(express.static(path));
      app.use("/", router);
      
      app.listen(8080, function () {
        console.log('Example app listening on port 8080!')
      })
      

      The finished app.js file will look like this:

      ~/node_project/app.js

      var express = require("express");
      var app = express();
      var router = express.Router();
      
      var path = __dirname + '/views/';
      const PORT = 8080;
      const HOST = '0.0.0.0';
      
      router.use(function (req,res,next) {
        console.log("/" + req.method);
        next();
      });
      
      router.get("/",function(req,res){
        res.sendFile(path + "index.html");
      });
      
      router.get("/sharks",function(req,res){
        res.sendFile(path + "sharks.html");
      });
      
      app.use(express.static(path));
      app.use("/", router);
      
      app.listen(8080, function () {
        console.log('Example app listening on port 8080!')
      })
      

      Save and close the file when you are finished.

      Next, let's add some static content to the application. Start by creating the views directory:

      Open the landing page file, index.html:

      Add the following code to the file, which will import Boostrap and create a jumbotron component with a link to the more detailed sharks.html info page:

      ~/node_project/views/index.html

      <!DOCTYPE html>
      <html lang="en">
         <head>
            <title>About Sharks</title>
            <meta charset="utf-8">
            <meta name="viewport" content="width=device-width, initial-scale=1">
            <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
            <link href="css/styles.css" rel="stylesheet">
            <link href='https://fonts.googleapis.com/css?family=Merriweather:400,700' rel='stylesheet' type='text/css'>
            <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
            <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
         </head>
         <body>
            <nav class="navbar navbar-inverse navbar-static-top">
               <div class="container">
                  <div class="navbar-header">
                     <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
                     <span class="sr-only">Toggle navigation</span>
                     <span class="icon-bar"></span>
                     <span class="icon-bar"></span>
                     <span class="icon-bar"></span>
                     </button>
                     <a class="navbar-brand" href="#">Everything Sharks</a>
                  </div>
                  <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
                     <ul class="nav navbar-nav mr-auto">
                        <li class="active"><a href="/">Home</a></li>
                        <li><a href="http://www.digitalocean.com/sharks">Sharks</a></li>
                     </ul>
                  </div>
               </div>
            </nav>
            <div class="jumbotron">
               <div class="container">
                  <h1>Want to Learn About Sharks?</h1>
                  <p>Are you ready to learn about sharks?</p>
                  <br>
                  <p><a class="btn btn-primary btn-lg" href="http://www.digitalocean.com/sharks" role="button">Get Shark Info</a></p>
               </div>
            </div>
            <div class="container">
               <div class="row">
                  <div class="col-md-6">
                     <h3>Not all sharks are alike</h3>
                     <p>Though some are dangerous, sharks generally do not attack humans. Out of the 500 species known to researchers, only 30 have been known to attack humans.</p>
                  </div>
                  <div class="col-md-6">
                     <h3>Sharks are ancient</h3>
                     <p>There is evidence to suggest that sharks lived up to 400 million years ago.</p>
                  </div>
               </div>
            </div>
         </body>
      </html>
      

      The top-level navbar here allows users to toggle between the Home and Sharks pages. In the navbar-nav subcomponent, we are using Bootstrap's active class to indicate the current page to the user. We've also specified the routes to our static pages, which match the routes we defined in app.js:

      ~/node_project/views/index.html

      ...
      <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
          <ul class="nav navbar-nav mr-auto">
              <li class="active"><a href="/">Home</a></li>
              <li><a href="http://www.digitalocean.com/sharks">Sharks</a></li>
          </ul>
      </div>
      ...
      

      Additionally, we've created a link to our shark information page in our jumbotron's button:

      ~/node_project/views/index.html

      ...
      <div class="jumbotron">
          <div class="container">
            <h1>Want to Learn About Sharks?</h1>
            <p>Are you ready to learn about sharks?</p>
            <br>
            <p><a class="btn btn-primary btn-lg" href="http://www.digitalocean.com/sharks" role="button">Get Shark Info</a></p>
          </div>
      </div>
      ...
      

      There is also a link to a custom style sheet in the header:

      ~/node_project/views/index.html

      ...
      <link href="css/styles.css" rel="stylesheet">
      ...
      

      We will create this style sheet at the end of this step.

      Save and close the file when you are finished.

      With the application landing page in place, we can create our shark information page, sharks.html, which will offer interested users more information about sharks.

      Open the file:

      Add the following code, which imports Bootstrap and the custom style sheet and offers users detailed information about certain sharks:

      ~/node_project/views/sharks.html

      <!DOCTYPE html>
      <html lang="en">
         <head>
            <title>About Sharks</title>
            <meta charset="utf-8">
            <meta name="viewport" content="width=device-width, initial-scale=1">
            <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
            <link href="css/styles.css" rel="stylesheet">
            <link href='https://fonts.googleapis.com/css?family=Merriweather:400,700' rel='stylesheet' type='text/css'>
            <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
            <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
         </head>
         <nav class="navbar navbar-inverse navbar-static-top">
            <div class="container">
               <div class="navbar-header">
                  <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
                  <span class="sr-only">Toggle navigation</span>
                  <span class="icon-bar"></span>
                  <span class="icon-bar"></span>
                  <span class="icon-bar"></span>
                  </button>
                  <a class="navbar-brand" href="/">Everything Sharks</a>
               </div>
               <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
                  <ul class="nav navbar-nav mr-auto">
                     <li><a href="/">Home</a></li>
                     <li class="active"><a href="http://www.digitalocean.com/sharks">Sharks</a></li>
                  </ul>
               </div>
            </div>
         </nav>
         <div class="jumbotron text-center">
            <h1>Shark Info</h1>
         </div>
         <div class="container">
            <div class="row">
               <div class="col-md-6">
                  <p>
                  <div class="caption">Some sharks are known to be dangerous to humans, though many more are not. The sawshark, for example, is not considered a threat to humans.</div>
                  <img src="https://assets.digitalocean.com/articles/docker_node_image/sawshark.jpg" alt="Sawshark">
                  </p>
               </div>
               <div class="col-md-6">
                  <p>
                  <div class="caption">Other sharks are known to be friendly and welcoming!</div>
                  <img src="https://assets.digitalocean.com/articles/docker_node_image/sammy.png" alt="Sammy the Shark">
                  </p>
               </div>
            </div>
          </div>
         </body>
      </html>
      

      Note that in this file, we again use the active class to indicate the current page.

      Save and close the file when you are finished.

      Finally, create the custom CSS style sheet that you've linked to in index.html and sharks.html by first creating a css folder in the views directory:

      Open the style sheet:

      • nano views/css/styles.css

      Add the following code, which will set the desired color and font for our pages:

      ~/node_project/views/css/styles.css

      .navbar {
          margin-bottom: 0;
      }
      
      body {
          background: #020A1B;
          color: #ffffff;
          font-family: 'Merriweather', sans-serif;
      }
      
      h1,
      h2 {
          font-weight: bold;
      }
      
      p {
          font-size: 16px;
          color: #ffffff;
      }
      
      .jumbotron {
          background: #0048CD;
          color: white;
          text-align: center;
      }
      
      .jumbotron p {
          color: white;
          font-size: 26px;
      }
      
      .btn-primary {
          color: #fff;
          text-color: #000000;
          border-color: white;
          margin-bottom: 5px;
      }
      
      img,
      video,
      audio {
          margin-top: 20px;
          max-width: 80%;
      }
      
      div.caption: {
          float: left;
          clear: both;
      }
      

      In addition to setting font and color, this file also limits the size of the images by specifying a max-width of 80%. This will prevent them from taking up more room than we would like on the page.

      Save and close the file when you are finished.

      With the application files in place and the project dependencies installed, you are ready to start the application.

      If you followed the initial server setup tutorial in the prerequisites, you will have an active firewall permitting only SSH traffic. To permit traffic to port 8080 run:

      To start the application, make sure that you are in your project's root directory:

      Start the application with npm start:

      Navigate your browser to http://your_server_ip:8080. You will see the following landing page:

      Application Landing Page

      Click on the Get Shark Info button. You will see the following information page:

      Shark Info Page

      You now have an application up and running. When you are ready, quit the server by typing CTRL+C. We can now move on to creating the Dockerfile that will allow us to recreate and scale this application as desired.

      Step 3 — Writing the Dockerfile

      Your Dockerfile specifies what will be included in your application container when it is executed. Using a Dockerfile allows you to define your container environment and avoid discrepancies with dependencies or runtime versions.

      Following these guidelines on building optimized containers, we will make our image as efficient as possible by minimizing the number of image layers and restricting the image's function to a single purpose — recreating our application files and static content.

      In your project's root directory, create the Dockerfile:

      Docker images are created using a succession of layered images that build on one another. Our first step will be to add the base image for our application that will form the starting point of the application build.

      Let's use the node:10 image, since, at the time of writing, this is the recommended LTS version of Node.js. Add the following FROM instruction to set the application's base image:

      ~/node_project/Dockerfile

      FROM node:10
      

      This image includes Node.js and npm. Each Dockerfile must begin with a FROM instruction.

      By default, the Docker Node image includes a non-root node user that you can use to avoid running your application container as root. It is a recommended security practice to avoid running containers as root and to restrict capabilities within the container to only those required to run its processes. We will therefore use the node user's home directory as the working directory for our application and set them as our user inside the container. For more information about best practices when working with the Docker Node image, see this best practices guide.

      To fine-tune the permissions on our application code in the container, let's create the node_modules subdirectory in /home/node along with the app directory. Creating these directories will ensure that they have the permissions we want, which will be important when we create local node modules in the container with npm install. In addition to creating these directories, we will set ownership on them to our node user:

      ~/node_project/Dockerfile

      ...
      RUN mkdir -p /home/node/app/node_modules && chown -R node:node /home/node/app
      

      For more information on the utility of consolidating RUN instructions, see this discussion of how to manage container layers.

      Next, set the working directory of the application to /home/node/app:

      ~/node_project/Dockerfile

      ...
      WORKDIR /home/node/app
      

      If a WORKDIR isn't set, Docker will create one by default, so it's a good idea to set it explicitly.

      Next, copy the package.json and package-lock.json (for npm 5+) files:

      ~/node_project/Dockerfile

      ...
      COPY package*.json ./
      

      Adding this COPY instruction before running npm install or copying the application code allows us to take advantage of Docker's caching mechanism. At each stage in the build, Docker will check to see if it has a layer cached for that particular instruction. If we change package.json, this layer will be rebuilt, but if we don't, this instruction will allow Docker to use the existing image layer and skip reinstalling our node modules.

      After copying the project dependencies, we can run npm install:

      ~/node_project/Dockerfile

      ...
      RUN npm install
      

      Copy your application code to the working application directory on the container:

      ~/node_project/Dockerfile

      ...
      COPY . .
      

      To ensure that the application files are owned by the non-root node user, copy the permissions from your application directory to the directory on the container:

      ~/node_project/Dockerfile

      ...
      COPY --chown=node:node . .
      

      Set the user to node:

      ~/node_project/Dockerfile

      ...
      USER node
      

      Expose port 8080 on the container and start the application:

      ~/node_project/Dockerfile

      ...
      EXPOSE 8080
      
      CMD [ "npm", "start" ]
      

      EXPOSE does not publish the port, but instead functions as a way of documenting which ports on the container will be published at runtime. CMD runs the command to start the application — in this case, npm start. Note that there should only be one CMD instruction in each Dockerfile. If you include more than one, only the last will take effect.

      There are many things you can do with the Dockerfile. For a complete list of instructions, please refer to Docker's Dockerfile reference documentation.

      The complete Dockerfile looks like this:

      ~/node_project/Dockerfile

      
      FROM node:10
      
      RUN mkdir -p /home/node/app/node_modules && chown -R node:node /home/node/app
      
      WORKDIR /home/node/app
      
      COPY package*.json ./
      
      RUN npm install
      
      COPY . .
      
      COPY --chown=node:node . .
      
      USER node
      
      EXPOSE 8080
      
      CMD [ "npm", "start" ]
      

      Save and close the file when you are finished editing.

      Before building the application image, let's add a .dockerignore file. Working in a similar way to a .gitignore file, .dockerignore specifies which files and directories in your project directory should not be copied over to your container.

      Open the .dockerignore file:

      Inside the file, add your local node modules, npm logs, Dockerfile, and .dockerignore file:

      ~/node_project/.dockerignore

      node_modules
      npm-debug.log
      Dockerfile
      .dockerignore
      

      If you are working with Git then you will also want to add your .git directory and .gitignore file.

      Save and close the file when you are finished.

      You are now ready to build the application image using the docker build command. Using the -t flag with docker build will allow you to tag the image with a memorable name. Because we are going to push the image to Docker Hub, let's include our Docker Hub username in the tag. We will tag the image as nodejs-image-demo, but feel free to replace this with a name of your own choosing. Remember to also replace your_dockerhub_username with your own Docker Hub username:

      • docker build -t your_dockerhub_username/nodejs-image-demo .

      The . specifies that the build context is the current directory.

      It will take a minute or two to build the image. Once it is complete, check your images:

      You will see the following output:

      Output

      REPOSITORY TAG IMAGE ID CREATED SIZE your_dockerhub_username/nodejs-image-demo latest 1c723fb2ef12 8 seconds ago 895MB node 10 f09e7c96b6de 17 hours ago 893MB

      It is now possible to create a container with this image using docker run. We will include three flags with this command:

      • -p: This publishes the port on the container and maps it to a port on our host. We will use port 80 on the host, but you should feel free to modify this as necessary if you have another process running on that port. For more information about how this works, see this discussion in the Docker docs on port binding.
      • -d: This runs the container in the background.
      • --name: This allows us to give the container a memorable name.

      Run the following command to build the container:

      • docker run --name nodejs-image-demo -p 80:8080 -d your_dockerhub_username/nodejs-image-demo

      Once your container is up and running, you can inspect a list of your running containers with docker ps:

      You will see the following output:

      Output

      CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES e50ad27074a7 your_dockerhub_username/nodejs-image-demo "npm start" 8 seconds ago Up 7 seconds 0.0.0.0:80->8080/tcp nodejs-image-demo

      With your container running, you can now visit your application by navigating your browser to http://your_server_ip. You will see your application landing page once again:

      Application Landing Page

      Now that you have created an image for your application, you can push it to Docker Hub for future use.

      Step 4 — Using a Repository to Work with Images

      By pushing your application image to a registry like Docker Hub, you make it available for subsequent use as you build and scale your containers. We will demonstrate how this works by pushing the application image to a repository and then using the image to recreate our container.

      The first step to pushing the image is to log in to the Docker Hub account you created in the prerequisites:

      • docker login -u your_dockerhub_username -p your_dockerhub_password

      Logging in this way will create a ~/.docker/config.json file in your user's home directory with your Docker Hub credentials.

      You can now push the application image to Docker Hub using the tag you created earlier, your_dockerhub_username/nodejs-image-demo:

      • docker push your_dockerhub_username/nodejs-image-demo

      Let's test the utility of the image registry by destroying our current application container and image and rebuilding them with the image in our repository.

      First, list your running containers:

      You will see the following output:

      Output

      CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES e50ad27074a7 your_dockerhub_username/nodejs-image-demo "npm start" 3 minutes ago Up 3 minutes 0.0.0.0:80->8080/tcp nodejs-image-demo

      Using the CONTAINER ID listed in your output, stop the running application container. Be sure to replace the highlighted ID below with your own CONTAINER ID:

      List your all of your images with the -a flag:

      You will see the following output with the name of your image, your_dockerhub_username/nodejs-image-demo, along with the node image and the other images from your build:

      Output

      REPOSITORY TAG IMAGE ID CREATED SIZE your_dockerhub_username/nodejs-image-demo latest 1c723fb2ef12 7 minutes ago 895MB <none> <none> e039d1b9a6a0 7 minutes ago 895MB <none> <none> dfa98908c5d1 7 minutes ago 895MB <none> <none> b9a714435a86 7 minutes ago 895MB <none> <none> 51de3ed7e944 7 minutes ago 895MB <none> <none> 5228d6c3b480 7 minutes ago 895MB <none> <none> 833b622e5492 8 minutes ago 893MB <none> <none> 5c47cc4725f1 8 minutes ago 893MB <none> <none> 5386324d89fb 8 minutes ago 893MB <none> <none> 631661025e2d 8 minutes ago 893MB node 10 f09e7c96b6de 17 hours ago 893MB

      Remove the stopped container and all of the images, including unused or dangling images, with the following command:

      Type y when prompted in the output to confirm that you would like to remove the stopped container and images. Be advised that this will also remove your build cache.

      You have now removed both the container running your application image and the image itself. For more information on removing Docker containers, images, and volumes, please see How To Remove Docker Images, Containers, and Volumes.

      With all of your images and containers deleted, you can now pull the application image from Docker Hub:

      • docker pull your_dockerhub_username/nodejs-image-demo

      List your images once again:

      You will see your application image:

      Output

      REPOSITORY TAG IMAGE ID CREATED SIZE your_dockerhub_username/nodejs-image-demo latest 1c723fb2ef12 11 minutes ago 895MB

      You can now rebuild your container using the command from Step 3:

      • docker run --name nodejs-image-demo -p 80:8080 -d your_dockerhub_username/nodejs-image-demo

      List your running containers:

      Output

      CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES f6bc2f50dff6 your_dockerhub_username/nodejs-image-demo "npm start" 4 seconds ago Up 3 seconds 0.0.0.0:80->8080/tcp nodejs-image-demo

      Visit http://your_server_ip once again to view your running application.

      Conclusion

      In this tutorial you created a static web application with Express and Bootstrap, as well as a Docker image for this application. You used this image to create a container and pushed the image to Docker Hub. From there, you were able to destroy your image and container and recreate them using your Docker Hub repository.

      If you are interested in learning more about how to work with tools like Docker Compose and Docker Machine to create multi-container setups, you can look at the following guides:

      For general tips on working with container data, see:

      If you are interested in other Docker-related topics, please see our complete library of Docker tutorials.



      Source link