Forewarning: For this to work, I suggest you have a rudimentary familiarity with Powershell. I believe this also requires an active NI SSP agreement, but you might be able to work around that.
OK, for those who don’t pay attention outside the LabVIEW community, using Docker for continuous integration is all the rage. Why? Well, a variety of reasons. For one, the ideal state is having a different build “machine” (or virtual machine) for every project you work on that builds it, and only it. Traditionally that hasn’t been a possibility.
Even if you did have the hard drive space, RAM and processing power to keep all of these VMs going and ‘at the ready,’ you’d be broke by the time you bought enough Windows licenses to cover them all. (Yes, I know about MSDN subscriptions. I’m also aware that Windows 10 will work forever without being activated; let’s be honest though, you don’t want people pirating YOUR software, so don’t do it to Microsoft.)
This is where Docker comes in. Essentially, rather than traditional virtual machines, which run completely independent images on top of your base OS (or beside your OS), Docker uses “containers” that sit on top of your base OS and reuse parts of it. I’m not going to go too far in the weeds of the details, but googling Docker vs. VM brings up some pretty good explanations.
So, if Docker is this great thing, why aren’t we using it with LabVIEW? Well, most languages out are either designed to be used from a command line, or descend from a language that did. LabVIEW wasn’t. And why should it have been? The whole POINT is that it’s graphical. It wasn’t until MANY years after LabVIEW was designed that continuous integration became a concern, and only then did we have a legitimate use case for a LabVIEW command line interface (CLI). We can’t blame NI for not being able to see 20+ years in to the future when designing a product.
Now, that said, in recent years, NI and others have done work towards creating CLIs for LabVIEW. NI ships one, but personally, I’m a fan of the G CLI from James McNally. Those make it possible to effectively use LabVIEW in a CI environment. (Yes, LabVIEW did have a primitive CLI before that, but both of these are much more useful.)
That doesn’t help us with the install process though. Before NI Package Manager entered the picture, this was possible, but required jumping through several more hoops. Lots of arguments on installers, and finger crossing they had been designed in a manner that worked without a GUI. NIPM (specifically nipkg.exe) makes this, frankly, simple!
Before we go too far, let’s acknowledge one thing: Docker isn’t that special. By and large, you can just treat it as a Windows instance without a GUI. That means that if you get stuck, just go try the same process on a “real” machine and you’ll probably be able to figure out what’s going wrong or what dialog is popping up.
Let’s also acknowledge that 90% of this is just following already documented instructions and despite hours of effort to find a workaround on my end, the only solution for the other 10% came from NI.
So, without further justification as to why, here’s how. (Note that throughout, orange represents the VM host, purple represents the docker instance and green represents the docker host.
Get Ready!
So, the first step is to get a Windows 10 machine and Install Docker Desktop for Windows. To be clear, this can be done on a Windows 10 VM running on Hyper-V, provided you enable pass through of virtualization extensions. (More on that in a bit.) It may also be possible with other virtualization platforms, but I have no idea how to do it. It’s also possible on Windows Server platforms, but I haven’t tried that either.
I’m running inside a Hyper-V VM, so my first step was to enable nested virtualization.
This is done from powershell on the bare-metal machine:
Set-VMProcessor -VMName <VMName> -ExposeVirtualizationExtensions $true
You may also need to set up a NAT virtual switch or clone your MAC. The above link walks you through this, but recent Windows 10 builds seem to have NAT virtual switches by default.
Now, you’ll also want to set your hyper-v virtual machine to not use dynamic RAM. It technically works, but it will stop resizing when you spin up a container. Setting it to a static number is just easier and safer. I chose 32 GB, but you can start lower and adjust as needed. Note that your virtual machine must be shut down to do this.
Now, download and install Docker. I used “stable,” but you’re welcome to live on the edge if you want. When installing, you will want to say yes when asked to “Enable Hyper-V Windows Features.”
Now that you have it installed, switch to Windows Containers. (If you want to get in the weeds here, Windows 10 uses “Hyper-V isolation to run containers by default. You can look elsewhere for the technical details, but that helps us because if the host OS chooses to upgrade, it doesn’t break the container image.)
Finally, fire up powershell and pull the most recent Windows container build that is compatible with your host. For me this was 1909. (You can determine this by hitting the start menu and typing “winver” which will bring up a dialog. I’m willing to bet that most of us are either on 1909 or 1903.
The docker command is:
docker pull mcr.microsoft.com/windows:1909
If you a trying to pull a container image that’s newer than your host OS, you’ll get an error something like this:
no matching manifest for windows/amd64 10.0.18362 in the manifest list entries
This will require a docker login, and the download is ~6GB, so go grab a coffee (or other beverage, depending on the time of day. Or your standards. Either way, no judgement here.) Note that Windows Server Core is smaller and might work, but I haven’t tried it (yet). I’m relatively confident Nano Server and IoT won’t work.
After that’s done, you’ll need to set up a mechanism for transferring files in and out of the docker container. I prefer to use “volumes” for this. Create one using the following command:
docker volume create shared
This will create the following folder on the host: C:\ProgramData\Docker\volumes\shared\_data
Now, go ahead and spin up your container. The command should look something like the following:
docker run --name labview2020 -v shared:c:\shared -it mcr.microsoft.com/windows:1909 powershell
This will generate a container named “labview2020” with volume you just created mounted at c:\shared on the container’s file system. It also opens an interactive powershell session after it is up and running.
Get set!
There are several ways of doing this, which changed completely between LabVIEW 2018 (pre-NI Package Manager) and LabVIEW 2019 (NI Package Manager). We’re going to be installing LabVIEW 2020, which is the easier of the two routes.
I’ve gotten this to work with 2017, so for older installs, the following link should walk you through enough of the basics to put the pieces together: https://knowledge.ni.com/KnowledgeArticleDetails?id=kA00Z0000019Ld6SAE&l=en-US
For NIPM installs, the following link is the “source of truth” but we’re going to go a little off script:
http://www.ni.com/documentation/en/ni-package-manager/latest/manual/automate-installer
The first (and only) thing you’re going to need to download from the NI website is NI Package Manager. I used the offline installer, which can be found by clicking the link in the bottom right of the page. The standard (online) installer would probably work as well, but I haven’t tried it.
After downloading, put it in the _data
directory shared volume you created: C:\ProgramData\Docker\volumes\shared\_data
Now, let’s go back to that powershell window that is inside your docker container. If you’ve closed it, you can just run docker attach labview2020
on the host and it should come right back.
Inside the docker container, navigate to the shared director and execute the NIPM installer. This will look like the following:
cd c:\shared
Start-Process -wait .\NIPackageManager20.0.0.exe -Argumentlist '--passive',' --accept-eulas','--prevent-reboot'
Note that when you get to the NIPM file name you can just type the first few letters and hit tab; it will autocomplete the name. You’ll still need to enter all the arguments. Let it do it’s thing. It should eventually return to the Powershell prompt.
Now, go to a machine that already has LabVIEW 2020 installed. Open a Powershell (or CMD) window and navigate to c:\Program Files\National Instruments\NI Package Manager and run the following:
.\nipkg.exe feed-list ni-labview*core*
This should bring up a short list of feeds. Look for the one that says ni-labview-2020-core-x86-en-2020-released
and copy the link that appears to the right of it: https://download.ni.com/support/nipkg/products/ni-l/ni-labview-2020-x86/20.0/released
(Note, that in Powershell, copy involves using the mouse to highlight the desired text and right-clicking. Paste simply requires a right-click with nothing selected)
Now, go back to your container’s powershell, navigate to the NIPM directory described above, and run the following:
.\nipkg.exe feed-add https://download.ni.com/support/nipkg/products/ni-l/ni-labview-2020-x86/20.0/released
.\nipkg.exe update
Now we should have access to the LabVIEW package. We should be able to run the following and have it be smooth sailing from here (spoiler alert, it doesn’t quite work):
.\nipkg.exe install ni-labview-2020-core-x86-en --accept-eulas --include-recommended
Unfortunately, the VI Package manager installer doesn’t (currently) play nice with Docker. (I think) that when it tries to register it’s auto-update Task with windows, the default Docker container’s username causes some problems. I can’t fault them for that, given that this is (to my knowledge) a completely new and unexplored area. Furthermore, I made JKI aware of this issue and they expressed they are “committed to helping make this work in upcoming releases.” They even have an Idea Exchange post for it. That’s exciting, but for now, however, we have to work around it. So, let’s do the following, entering ‘y’ whenever asked for permission to continue. (Note that you CAN use the –yes flag, but I’d rather review each step as we go.)
.\nipkg.exe install ni-labview-2020-core-x86-en --include-recommended --accept-eulas
... ignore some error messages ...
.\nipkg.exe install ni-labview-2020-core-x86-en --accept-eulas
.\nipkg.exe install mgi-solution-explorer ni-certificates ni-labview-2020-database-toolkit-x86 ni-labview-2020-datafinder-connectivity-x86 ni-labview-2020-report-generation-toolkit-x86 ni-labview-command-line-interface-x86 ni-syscfg-labview-2020-support-x86 ni-webservices-amqp-labview-2020-support-x86 --accept-eulas
Now, we’re most of the way there, but resist the temptation to go open LabVIEW just yet. I assume you’ll also want a few more things. First, let’s install VI Analyzer.
.\nipkg.exe feed-add https://download.ni.com/support/nipkg/products/ni-l/ni-labview-2020-vi-analyzer-toolkit-x86/20.0/released
.\nipkg.exe install ni-labview-2020-vi-analyzer-toolkit-x86 --accept-eulas --include-recommended
I’d also reccomend installing Wiresmith Technology’s G CLI. Now, since we don’t have VIPM on the docker container, we have to get a little creative. You could go download the package from the NI Tools Network and brute force it by extracting directly. I chose a little bit cleaner approach.
Go to a machine with a functioning VIPM distribution and install the G CLI to LabVIEW 2020. This will take care of all of our mass-compile issues for us. Now, on that machine, navigate to <vi.lib>
and copy the G CLI Tools
and Wiresmith Technology
folders to a folder named vi.lib
in your “shared” volume.
Back in your docker container, navigate to your vi.lib:
cd 'C:\Program Files (x86)\National Instruments\LabVIEW 2020\vi.lib'
Copy the relevant files:
Copy-Item -path "C:\shared\vi.lib\*" -Destination ".\" -Recurse
Finally, install the piece that makes G CLI work:
cd '.\Wiresmith Technology\G CLI\Installation Support\'
Start-Process -wait .\G_CLI_x64.msi -Argumentlist '-q','-ls install_log.txt'
Make sure the install was clean:
cat install_log.txt
Should produce a log that ends as follows (with timestamps appropriate for your situation):
...
…
Action ended 12:25:30: PublishFeatures. Return value 1.
Action start 12:25:30: PublishProduct.
Action ended 12:25:30: PublishProduct. Return value 1.
Action start 12:25:30: InstallFinalize.
Action ended 12:25:30: InstallFinalize. Return value 1.
Action ended 12:25:30: INSTALL. Return value 1.
MSI (s) (A8:EC) [12:25:30:361]: Product: G CLI -- Installation completed successfully.
MSI (s) (A8:EC) [12:25:30:361]: Windows Installer installed the product. Product Name: G CLI. Product Version: 2.3.0.6.
Product Language: 1033. Manufacturer: G CLI Contributors. Installation success or error status: 0.
=== Logging stopped: 6/4/2020 12:25:30 ===
Delete the install log:
rm .\install_log.txt
The special sauce
We’re not quite there yet. Restrain that urge to open LabVIEW. You see, LabVIEW still isn’t licensed, and will just hang if you try. On normal machine, all you have to do is fire up License manager, log in, then activate. Activation can be done easily enough from the command line, however, there is absolutely no NI supported way of logging in via the command line (and after about a week’s worth of efforts, I couldn’t find an unsupported way that worked in a docker instance either).
They are aware of that issue and are interested in addressing it in the future. In mean time, it turns out that if LabVIEW is licensed, you don’t have to log in to license manager. Sounds like chicken-and-egg situation, right? Fortunately for us, one of my favorite breakfasts is an omelet with shredded chicken.
Navigate to the license manager directory and export your computer ID:
cd 'C:\Program Files (x86)\National Instruments\Shared\License Manager\'
.\NILicensingCmd.exe /exportcomputerid
Retain the alpha-numeric string this outputs. It should be four sets of four characters separated by hyphens.
Go find the license files:
cd 'C:\ProgramData\National Instruments\License Manager\Licenses'
Copy the following files to your shared directory, using the copy command for each (Copy-Item -path ".\<filename>" -Destination "c:\shared\"
):
- LabVIEW_AppBuilder_PKG_200000.lc
- LabVIEW_DatabaseTK_PKG_200000.lc
- LabVIEW_PDS_PKG_200000.lc
- LabVIEW_ReportGenTK_PKG_200000.lc
- LabVIEW_VIAnalyzerTK_PKG_200000.lc
Now delete the contents of the Licenses directory (leaving the directory itself:
rm .\*
Here’s where the SSP comes in. Open a service request with NI requesting licensing help, and provide the TSE with the following link, which will help them through the process (note this is only visible to NI employees): http://bit.ly/lvdockerlicense
The procedure for start an SR here in the US requires starting on the web via the Service Request Manager. It may be possible to get activation assistance without SSP, but I leave that as an exercise for the reader if needed.
- Provide the license files you copied to an NI TSE.
- After contacting a TSE, provide them with the computer ID of the docker image and your serial number
- Explain you need a signed license file. Remind them to set the expiration date as appropriate for your account.
At this point, they should be able to e-mail you the signed license files you need.
While you’re waiting, you’ll need to restart your docker instance. Type exit
in the Powershell window, which should return you to the host. Then type the following:
Docker start labview2020
Docker attach labview2020
This will put you back in the docker container. When you get the e-mail, copy these new files back to the original location in ProgramData.
GO!
Finally:
PS C:\> g-cli quitlabview.vi
If everything went right, you should get the following in return:
Closing LabVIEW in 1000ms
From here, it’s just like you’re dealing with any other LabVIEW CI instance. More on that in a future post.
Future work
So, to be clear, I’ve gotten the basics functioning, but there are still some kinks to work out. In an ideal world, most of this would be automated with a Docker file. I also haven’t tested every possible corner case (or even every likely corner case), so there are no guarantees here, expressed or implied. I also haven’t ventured in to the land of k8s or registries, or many other things that are common (arguably necessary) in the Docker workflow. In short, this is only a start.
Acknowledgements
I would be terribly remiss if I didn’t thank the following people for their assistance with this process:
- My colleagues and supervisors at Hiller Measurements for their support and willingness to humor me
- Eric Reffett – Chief Product Planner at NI: Eric is interested in CI workflows in general and is always open to discussing ways to improve them. I was stuck on the activation part, and Eric could easily have told me this was unsupported and left me to flounder on my own. Instead he offered assistance and we got through it.
- James McNally for some G CLI troubleshooting assistance (Restarting your Powershell instance is mandatory.)
- The NI License Manager team
- Hans Acuna – NI TSE who generated the license files for LabVIEW 2020
- Chase Easterling – NI TSE who helped me out the first time I tried this