Running Hercules and MVS3.8J in a Container
First why would you want to ?. There are many use cases, a few examples would be
- To play with things that might break it
Anything destructive can be tested against the container instance and if anything goes wrong the container just deleted and restarted
Yes you could recover outside a container environment by just deleting all the shadow files, but I always find myself wondering if their was something I should have merged - To easily run lots of instances of MVS3.8J on a single machine without needing
lots of seperate config files and copies of dasd all over the place, many can
be run on a single machine as I cap mine at a tiny 250Mb memory each and they run fine.
My use case for needing many was playing with NJE38 where I could just spin up as many containers as I needed with complex routing configurations set just using container environment variables to edit the hercules conf file before starting it - To easily move your play environment around
You can load the container image onto any machine you have docker installed on, in my case I have a local docker registry so just need a docker run command to get a container running
Are there any benefits to using a container ?. For me yes, one container image can be customised in many ways at run time which suits my needs better than manually editing config files when I want to run it in a different configuration.
My workflow would be
- anything new or that might break something I test in the container; I keep the JCL and source code etc. as text files on a dev machine and submit (via the 3505 socket card reader) those to the container to create any needed datasets and run compiles etc. to test things
- Only when I am happy with them will I run the same JCL decks against my "master" on disk system, merge the shadows files into the main disks, and build a new container image
- push the new container to my local docker registry
- on any machines with docker, simply pull the new container image and it is on all my machines.
Containers will not be suitable for everyone, and if you are only interested in running MVS3.8J to play with rather than to make any major changes to then there is a supported CE container that would suit you better that may still be available at the MVS/CE github location https://github.com/MVS-sysgen/sysgen
Limitations, not really limitations with a container are before you mention them
- what if something goes wrong and I need a console ? - from TSO both SPY and IMON option O
can provide access to a console (note in TK4- a few commands need to be entered to
get anything but a blank screen on those), and if TSO is dead you can also
'exec -ti' into the container and acces the two actual 3270 consoles by
reconnecting to one of the "screen" sessions connected to those consoles
Note: yes this could be a pain in devinit for tapes, options there are automate tape mounts (I have my MMPF utility available on this site that amoungst other things automates tape mounts and manages scratch/in-use 'managed' volumes) or a devinit can be issued from a JCL job (most mvs3.8j systems come with HERCCMD, and available from this site is also my MDDIAG8 [as I like having source code for things like that so wrote one]) so I just have a job in a proclib that can be run for 'unmanaged' volumes from TSO with a simple 'S procname,u=nnn,v=tapvol' so that also is not really an issue - when I punch or print something it is no use if it is only within the container - is also not an issue as you can simply map container directores to your containers host filesystem (I personally changed the directory structure and hercules config a little so pch/prt/log all live under one directory requiring only one mapping)
What you need to create your own MVS3.8J container
At a minimum you will need
- (optinal) a recent copy of hercules, there are two copies you can choose from,
spinhawk or hyperion. I have had issues with hyperion (containers not portable between
rhel and debian) so use spinhawk for all my containers now.
Optional as hercules may also be available in the respos for the Linux distribution you intend to base the container on but these are seldom up-to-date and are probably based on the main hercules-390 code which will not have extensions added to the other two; so I find it easier to have compiled a known copy of the hercules application
Note: compile on a machine with the same OS you will base the container on if not using one from the distribution repos - a working local copy of MVS3.8J running under hercules to place into your container. You should have it tested running against the version of hercules you intend to use
- a fully tested startup script that will start your copy of hercules in a "headless"
totally non-interactive (and non GUI) way.
You may already have one if you have been using hercules for a while. If not I provide one further down based on what I have been using for over a decade that use screen sessions for (as scripts can provide input to them) and c3270 sessions for the consoles (as they have a scriptports that scripts can use to issue commands into the c3270 sessions as it they were being typed manually) and that combination allows any commands that need to be issued to hercules or the consoles to be scripted - optional but pointless creating a container without it is a configuration
script that will use environment variables passed to the container to reconfigure
you hercules and MVS3.8J system for any dynamic configuration you want.
The example container startup wrapper script I provide further down just shows how to use a environment variable passed to docker to update the tzoffset in the hercules config file before starting hercules.
Customisation options are unlimited, I do things like editing the hercules config file to customise devices (add/change, comment out, change ipaddr and port numbers etc) before starting hercules and also submit JCL to further customise once it is running also based on environment variables [not covered in my examples here as my wrapper script is huge, but the single example of customising the tzoffset gives you a starting point for your own customisations] - Ideally you would also have a container health check script within the container, I provide an example one here which is based on my customisations of TK4-
- docker (or podman) installed of course, to create the container
- and a Dockerfile to build the container and copy all the above into it as part of the build, I have included an example one also
- Totally optional would be a local docker registry to store your images in
- Decide on a UID to use. By default useradd will start creating users using UIDs in a range a normal system does, which will obviously result in processes owned by the container showing as owned by whoever has that UID on the physical host machine which is not ideal as that user can also kill them. Any users I create for containers I will explicitly create in UID ranges from 6000 up (and optionally also create a user with a matching UID on the host machine so a ps shows what it is rather than an orhpan UID
- Decide on your directory layout with what you may intend to map to the host drive; for example I have my punch output files (and one print device output) in its own directory as for my workflow if editing large source code members I would punch them out, use "vi" to edit and submit test changes, and just submit a job to put the final working code back into the PDS (not only because the TSO editors have issues editing huge members and each save takes up space in the PDS; but because I prefer "vi" as my editor).
There are a few things to note, the major one being that when doing the docker build everything is expected to be under your working directory. For example you cannot copy your hercules dasd etc. from "/some/other/dir" it must be from "dir" under your work directory. That is a pain, but as my configs etc. for a container are different from my on-disk hercules system and static I just copy any changed dasd from my real system into the dasd directory under my build directory as needed without changing anything else.
A hands-off startup script is needed
The entire point of a container is that you just run it and everything is available; there must be no need to enter any commands. So you need a hands-off startup script that will start everything up, replying to any prompts needed by hercules or the consoles, and ending up with a running container you can simply start a TSO session to and start using it.
If you have been using hercules to run MVS3.8J for a while you probably have one. If you are using one of the 'packages' that expects a desktop and fires up lots of x3270 sessions that will not work, you do not want lots of GUI desktop packages in a container and my example Dockerfile is to build for a headless environment.
In the examples my example startup script is copied into place by my example Dockerfile as container_start_system.sh, and is designed for TK4-; however it was also used without issue on a normal TK3 system (less device 01F and one screen session) as that is what it was created for origionally.
A CMD entry point script that never ends
When the container starts up it will run a command as defined in the CMD entry, that is expected to start the 'critical service' the container provides and should it stop running (ie: when the a script completes and ends) that is considered a fatal error for the container. So whatever CMD startup script you use must after it has done all its work keep running, going into a endless loop to sleep for a few hours and loop back to sleeping for a few hours achieves that.
While not best practise, as that script must run, I also use that to perform
all customisation of the hercules configuration file devices (based on environment
variables passed to the container) prior to this script actually running the
script above that will actually start hercules and the MVS3.8J system.
This allows things like ip-addresses and ports on devices in the hercules
config file to be easily customised allowing multiple copies of the container to run
on a single host without port conflicts for things like CTC device ip connections.
I also use mine to based on environment variables passed to the container submit
a JCL deck after hercules is started to perform further customisations, such as
creating a new NJE38 config to match all the device editing done.
However the example I provide here just allows customisation of the tzoffset variable in the
hercules config file to show it is possible (the huge script I have is unlikely to match
you needs so just a small example).
But customisation options are only limited by your imagination; and possibly an environment
variable limit I have not hit yet.
Lets also have a health check script
While having a health check script is of course optional, for running MVS3.8J in a container with a hands-off startup it provides the following benefits
- when it changes from "starting" to "healthy" it is safe to start a TSO session; trying to start one too early may grab a console connection and interrupt fataly the hands-off startup needing the container stopped/started again to recover
- if anything does go wrong during the days/weeks you have it running the when if changes to an "unhealthy" state you will know something has gone wrong; and depending on what container manager you are using it can be automatically restarted
I have included an example healthcheck script based on my TK4- customised environment. It makes sure hercules and mvs38j are running OK, by checking the number of screen sessions running are correct, that the 3270 (tso) and 3505 (card reader) ports are listening, and uses curl to check port 3270 responds to connections to make sure that is not hung and TSO should be available.
The important thing to note in the hercules environment which you will see in my Dockerfile example is that there must be a huge delay before the first healthcheck poll. That is to give time for hercules to actually startup so the healthcheck script does not interfere with the startup, there are a lot of 'sleep' commands in my startup script to let things start in a staggered/clean order and we want to wait for the startup to copmplete fully.
The easy bit, the Dockerfile
First, do not use EXPOSE lines in your Dockerfile. If used they will start a
docker-proxy process (if using docker) for every port listed and just waste machine resources. Using
port mapping ensures only the ports you need are opened and have processes running for them.
And that is important ! as if you want two containers running on the same
machine you do not want each container trying to use/expose the same ports; so always rely
on mapping rather using expose within the container.
Ok, by now you should have a hands-off startup script, a CMD wrapper script, and a health check script in place. Now you can actually build a Dockerfile and create your image.
The Dockerfile I use myself is a little more verbose than the example I provide
here as I copy locally compiled hercules binaries in and a build script to generate
my Dockerfile depending on whether I am building for debian or fedora and whether
I am using spinhawk or hyperion binaries etc.
You do not care about any of that; my example Dockerfile provided here just assumes
you are going to use the distribution repo version of 'hercules' and debian:12-slim
as the base for the cntainer to keep the example simple.
Also note that a temporary build environment target is used, and the near final step is to copy that as a single layer to the final container image; with the size of the dasd volumes that simple procedure can shave 500Mb off the size of the final container image.
My example Dockerfile is available here for a quick look at it, but it is also included in the example TGZ file (keep reading) along with all the scripts so don't cut/paste this one.
The startup script(s) needed
I have two of these. The history behind that is that for decades I have had a headless startup script for my installs of mvs3.8j and as it just works I chose to use that with mimimal changes to save a lot of debugging within the container.
The second script is a startup "wrapper" script required, and because it can do so much is a lot larger messier script. The main reasons it is required are
- the CMD for the container must never stop running or container startup is considered to have failed, so at the end of this script it can just go into a loop of sleep a few hours and repeat to stay running
- it can use all the environment variables passed to the container to perform any customisations to the hercules configuration file you require before it actually starts hercules... which is a requirement because if you are going to run multiple containers on one host you do want to be able to customise them (probably not an issue for most 'default install' users that do not have interlinked systems but if you are configuring devices to connect to remote mvs3.8j instances (ie: using NJE38) you want to be able to customise them at container runtime)
- I also use my wrapper script to check for customisations that will be needed after the mvs3.8j system is up and running. So if required it will build a JCL job to do things like update parmlib members to match customisatins made to the hercules config file based on environment variables passed to teh container
- and it can be used to determine how it is running. Docker and Kubernetes behave slightly differently in that in many cases the default user would be root; Kubernetes can have a runas user set to change that but the container wrapper script needs to run the main startup script differently based on whether it needs to sudo/su/do-nothing do get to the correct userid
A headless startup script is needed
The actual startup script needs to be able to start hercules and the mvs3.8j system including replying to any hercules and console prompts needed.
My startup script I have tweaked slightly between TK3 and TK4-, little things
like the IPL volume changing, config file and directory locations; but those are
set as variables at the start of the script so easily customised for any site
by just changing a few lines.
Although there were a few more tweaks to make it
usable in the container so consider my example as a container script rather
than a headless startup script.
It is important to note that in my examples I have the following consoles defined and the startup script is coded to expect these. These are a slightly tweaked setup from the TK4- distribution (TK3 from memory did not use 01F and had 009 as the telnet hardcopy interface and no console attached to the hercules interface itself)
# 3270 consoles on 010 and 011 # 009 is used by hercules console interface # 01F is a telnet hardcopy interface 0009 3215-C 0010 3270 CONS 0011 3270 CONS 001F 3215
It will run after the initial wrapper script has done any customistions to the hercules config file needed to
- create a rc file witha delay and the ipl command
- use 'screen' to connect 3270 and telnet console sessions needed
- wait quite a bit for the delay in the rc file and ipl to happen
- reply clpa on the first console, using the c3270 script port
- wait quite a bit for the system to start up
- issue the switch to roll-delete to both c3270 consoles, using the c3270 script ports
- and should be ready to use
A working example of this script is in the TGZ file so read on.
A startup wrapper script is needed
OK you could stuff all the wrapper stuff into the hercules/mvs38j startup script but I have decided to keep all the customisations needed in a seperate script as most customisations need to be done before hercules is started.
My container wrapper start up script uses environment
variables passed to the container to customise the hercules
configuration file before hercules is started; things like enabling/configuring
devices or commenting them out in the hercules configuration file.
It also uses the environment variables to build a JCL deck to submit to the
running mvs38j system after it is started to update any parmlib configurations
that may be needed in conjunction with the changes made.
You don't care about any of that so what I provide here is a simple wrapper
script that only has as an example how to customize the tzoffset in the
hercules config file as that is all most people would need (if you are
somewhere that does daylight saving time).
It also has placeholders for waiting for startup to complete and submitting
a JCL deck for after startup customisations.
This is the container CMD script which must never stop running so has the endless sleep a long time and loop back and sleep again to keep it running.
A working example of this script is in the TGZ file so read on.
OK so you just want to build a container
You probably do not care about all the above and just want to create a container for your existing MVSS3.8J environment. The impatient don't care about how or why and if read this far just want to create a container immediately, which is what the TGZ file here is for.
There are a few things you will have to edit, for example your mvs3.8j directory name is probably different to mine. The two places to customise are the Dockerfile itself, and an environment conf file that is used by all the bash scripts (so no need to edit any bash scripts themselves, just the variables they use).
What is needed to be changed is covered in a README.txt file and in comments at the start of the Dockerfile and in the envirnment config file.
Simply download the TGZ file, tar -zxvf it in the directory immediately
above your mvs3.8j directory (will create a new directory called container_scripts),
make the very few lines of editing needed in the Dockerfile and environmemt config file,
copy the Dockerfile up into the higher level directory (so your mvs3.8j directory and
the container_scripts directory are immediately below the Dockerfile), and
run the docker build command. At which point you have your container and can
simply run it under docker/podman etc. You should have a container running your
setup in under 10mins.
This is the TGZ file you have been waiting for
I also test my containers under miniKube, because it is fun to make sure it works there as well.
Or if you really cannot be bothered details on my heavily customised pre-existing docker image may interest you; although as that has all the DLIBs and CBT volumes it is huge.