You are correct, apt-get upgrade will of course upgrade packages in the future, but what you are trying to do with the Dockerfile is to build the docker container in the correct state. After the docker image is built, it's frozen, and the apt-get upgrade won't run anymore.
If you re-build your image, well, then of course the packages are updated, well, don't use apt-get upgrade!. You only need to run apt-get update to refresh the package list.
So what you need to do is to run apt update when building, then install specific versions of each package you need:
apt-cache madison openssl
openssl | 1.1.0f-3+deb9u2 | http://deb.debian.org/debian stretch/main amd64 Packages
openssl | 1.1.0f-3+deb9u2 | http://security.debian.org/debian-security stretch/updates/main amd64 Packages
So, to install openssl 1.1.0f-3+deb9u2 , do
apt-get install openssl=1.1.0f-3+deb9u2.
You would need to do this for each and every package that needs a specific version.
Another way could be to not run apt-get update at all, but with Debian, packages are upgraded because of security issues, then are no longer available in the regular distribution sites, so that might break your docker build in the future. Depending on the way your scripts are built and the dependencies needed it might be worth a shot. Since it doesn't cost anything to build images (more than disk space), try both!
mainonly, Ubuntu LTS is probably a reasonable choice too (butuniverseis not reliably updated, that may be a no-no). As for Ubuntu, it might make more sense to start at the latest LTS release (18.04 as of now), as that will be maintained for 5 years from this year instead of 5 years from two years ago with 16.04. That bit is entirely about how long runway you have before you need to do a major upgrade to keep getting updates. Generally, it appears the slower moving distros best suit your desires, Debian, Ubuntu LTS, RHEL (/CentOS), etc. – Håkan Lindqvist Aug 01 '18 at 11:06