Why I implemented a Serverless Function in COBOL.
What you should know: I am currently working for Oracle as Principal Cloud Architect, but any posts on this blog reflect my own views and opinions only.
For these cases it might make sense to explore all the options you have with your serverless environment. In some cases it is rather easy to use a custom runtime for that language you need, in some cases a little more effort is needed.
- AWS. Lambda using a custom runtime.
- Azure. Azure Functions provides a sophisticated and easy to work with environment, but sadly it does not support custom runtimes. You are limited to a few languages – even Go is still missing. If you do not feel like contributing to Azure Functions source, the next best solution probably would be to use Azure Container Instances.
- GCP. The premier way to run a custom Docker container seems to be Google App Engine Flexible, although you will have at least one instance run 24/7. If you can do this with Cloud Functions, then at least i wasn’t able to find out how.
- OCI. As Oracle Functions are derived from fnproject, you can use some of their tooling for a highly customized runtime environment. All you need to do is prepare a Dockerfile containing you runtime and code and use the hotwrap binary as a wrapper to fulfill the fnproject runtime contract.
Due to my background i will explore the last option, so running COBOL code in an Oracle Function. I already wrote a few posts on Oracle Functions – in case you are new to this topic the posts on environment setup and writing a first function are a good starting point.
I have to admit i do not really know COBOL. With some guides and tutorials i finally managed to write a simple hello world program, that actually would compile and run. And here’s the code.
*******> Sample for running COBOL code with fn IDENTIFICATION DIVISION. PROGRAM-ID. FN_COB. ENVIRONMENT DIVISION. INPUT-OUTPUT SECTION. FILE-CONTROL. SELECT SYSIN ASSIGN TO KEYBOARD ORGANIZATION LINE SEQUENTIAL. DATA DIVISION. FILE SECTION. FD SYSIN. 01 LN PIC X(80). 88 EOF VALUE HIGH-VALUES. WORKING-STORAGE SECTION. 01 WS-STDIN GLOBAL PIC X(255). 01 W-IDX PIC 9(2) VALUE ZERO. PROCEDURE DIVISION. ACCEPT WS-STDIN. UNSTRING WS-STDIN DELIMITED ALL SPACE INTO WS-STDIN COUNT W-IDX IF W-IDX > 0 THEN DISPLAY "Hello, " WS-STDIN(1:W-IDX) "!" ELSE DISPLAY "Hello, World!" END-IF STOP RUN.
If you pass input on STDIN, it will return a friendly Hello and echo the input. If there is no input, it will just say “Hello, World!”.
For tinkering around i used the GNU COBOL implementation. It seems to be a great starting place to get an idea how COBOL works and seems to be working well with some of the existing code coming from other platforms and compilers.
Defining the Runtime
Since we now got the COBOL “application”, it is time to prepare the runtime. All we need is a Docker image based on Alpine Linux, a COBOL build and runtime environment and the fnproject hotwrap binary.
FROM alpine:latest WORKDIR /opt RUN apk add tar libaio libnsl libc6-compat autoconf make g++ gmp-dev db-dev libxml2-dev # get cJSON RUN wget https://github.com/DaveGamble/cJSON/archive/v1.7.14.tar.gz && tar xvfz v1.7.14.tar.gz && \ cd cJSON-1.7.14 && make && make install && cd .. # get and install gnucobol RUN wget https://sourceforge.net/projects/gnucobol/files/gnucobol/3.1/gnucobol-3.1-rc1.tar.gz/download -O gnucobol-3.1-rc1.tar.gz && \ tar xvfz gnucobol-3.1-rc1.tar.gz && cd gnucobol-3.1-rc1 && ./configure && make && make install && cd .. # get fn hotwrap COPY --from=fnproject/hotwrap:latest /hotwrap /hotwrap COPY ./src . RUN cobc -x func.cob CMD ./func ENTRYPOINT ["/hotwrap"]
Please note that GNU COBOL is built here with XML and JSON support. So for making real life functions this might be a sensible starting point. Just think of splitting up the image in a build and a run part to get down to a sensible size then.
After building and deploying the function, a simple test shows that everything works as expected. Serverless COBOL!
Apart from sounding funny, does this really make any sense?
I think this proof of concept does make sense. If you got some function that is stateless and does not need more than 300 seconds of execution time, than it is a candidate for a serverless function. And if you find that you need some very special runtime environment components that you would run in a Docker container, then it is a candidate for a custom runtime as described in this post. Just keep in mind: If trying to use proprietary runtimes, make sure to understand the licensing terms.
COBOL might be just an example for a rather uncommon runtime in todays cloud environments, but there for sure is a lot of custom code written in C, C++, Perl or Fortran. This approach might help removing one component of your landscape that is needed just to run that code. You might have a hard time if trying to run RGP code though;)