Category: Web

stuff about web design, development, trends, and tricks

  • PowerShell Regex Matching

    I was working on a project where I needed to understand the naming convention for the servers. Since they had been made by several teams over several years, there was no convention. It was a giant pain in the ass.

    I wrote a function that would accept the server name and then try to parse it out. While there was not a strong standard, there was a few soft standards I could guess at. It took a while to figure out but PowerShell -match returns an array, if you use regex groups. All I needed was a few regex patterns and then I could start to decode these server names!

    The servers were in different data centers, so some had DEN, ’cause they were in Denver data center (see?). But some were in CINC (with four letters, not even keeping three letters) as they were in the Cincinnati data center. The next few characters in the name gave some hint as to purpose; WEB, or SQL or something more obscure like OTOPS, again with varying number of letters. Last, the server could have a number, 01, 02, etc. or a letter, A, B, C if it was part of a set. But then we had a few that were part of 01 set but there were several of those so you got 01A, 01B, 01C.

    There are several great regex tools on-line to show you how your pattern is working and what the rules are. For the server names I wound up with:

    (?<datacenter>den|cinc)(?<role>\w+)(?<countNum>\d{2})(?<countLet>[a-d])

    PowerShell isn’t case sensitive, so we’ll ignore those differences. Also, I’m using named groups, those are defined with ?<datacenter> where the name of the group is data center. Then everything that matches within the ( ) for that group winds up in $Matches.datacenter. $Matches is a built-in variable and can be referenced by index number if not using named groups.

    switch -regex ($serverName){
      "(?<datacenter>den|cinc)(?<role>\w+)(?<countNum>\d{2})(?<countLet>[a-f])" {
        $datacenter = $Matches.datacenter
        $role       = $Matches.role
        $countNum   = $Matches.countNum
        $countLet   = $Matches.countLet
        break
      }
      "(?<datacenter>den|cinc)(?<role>\w+)(?<countNum>\d{2})" {
        $datacenter = $Matches.datacenter
        $role       = $Matches.role
        $countNum   = $Matches.countNum
        $countLet   = "None"
        break
      }
    }

    This is what I wound up with; the switch takes the name of the server passed to my function, checks it against a few different regex patterns, and where there is a match for all groups of the pattern, executes the code block.

    In the code block I’m pulling the named group and assigning to my values to spit out at the end of the function. Additionally, you see the second pattern is for when there are only numbers in the name. In the running code I had a few more patterns to match all the options used when creating servers. Note the break in the script block, that’s because the switch will keep matching patterns all the way down the statement! The second pattern will give me “None” for my count letter value and not an error or something unexpected.

    I didn’t figure all this out on my own, Kevin’s article on Regex showed me the way. You can get the details on named groups here.

  • Managing Containers with Portainer

    I’ve detailed how to get Portainer and a Portainer Edge agent installed on two separate hosts, but now what?

    To add containers, I think using Stacks is the way to go. You can see an overview of Stacks here. Additionally, if you are cool with the defaults, there are pre-built Stacks labeled as App Templates. These are Docker Compose files for common open-source sites and services that Portainer put together for you. I have made a few of these myself!

    These Stacks can be fairly generic and then you add in an ENV file to give your specific values. Of course you can also reference the standard environmental variables provided by Docker.

    The trickiest thing was figuring out the volumes. Sometimes I need something pointed to a Docker location, or something on my Docker host, but mainly I’m trying to keep critical, persistent data on my NAS. Reading thru the official documentation gave me some hints but it was StackOverflow to the rescue!

    o: addr=mediaserver,nfsvers=4,nolock,soft,rw

    In the options, you need the IP (or DNS name) of the NAS server, the version of NFS used, “nolock,soft” helps with asynchronicity, and you want to allow read and write.

    Once you have what is basically a Docker Compose file, you can just “add stack”, select or enter your details, add your environmental variables and values, and BANG, you have one or more containers!

  • How to install Portainer with multiple nodes

    How to install Portainer with multiple nodes

    Prerequisites

    To get multiple nodes, I am using both a VM on my host and Docker on my Synology. To that end, these instructions will be based off of what I am using.

    I’ll cover the steps from once you have Synology up and running as well as a Linux VM running Ubuntu. For sizing the VM, I couldn’t find much guidance so went with 4 cores on 1 CPU, 2 GB RAM and 10 GB HDD. I only have a two-bay NAS but did upgrade the RAM to 6 GB.

    Synology Steps

    I used the excellent steps on Synology: 30 Second Portainer Install Using Task Scheduler & Docker – Marius Hosting. The thing to remember in his steps, the names and paths created in the middle are used explicitly in the scheduled task. You are using the scheduled task so you don’t have to SSH into the Synology. Think of it as entering the commands in the terminal. Obviously I edited the commands to match my setup. I like Maruis’ way as it’s simple with the flexibility of command line. Speaking of which, I needed to adjust my Synology firewall to allow the traffic on the new ports. The script Marius provided uses 8000 and 9000.

    There is also a GUI interface in Synology for creating docker containers. This site has a great tutorial on that but uses PiHole as the example. In the end, I used the command line scheduled task (even for updating Portainer) as I don’t know how to use the GUI to get an equivalent to “docker.sock” volume.

    Ubuntu VM Steps

    First I created a new VM and installed the latest mini-Ubuntu. There is a selection in the installer for adding Docker, don’t do that. It loads Snaps and those don’t work. Then I installed Docker by following these instructions- How to install Docker on Ubuntu as they were using more of the built-in functionality of APT.

    Next I installed the agent using these instructions. The guy talks a lot but the key point of using the Edge Install script provided in Portainer is correct. The link should be about 3 min in where he really gets to the details.

    Now I have Two

    Now I have two environments in one administration interface! For my purposes, I’m going to put more containers on the VM to keep the load over there.