Recently there were discussions and blog posts on Docker for Pharo and Gemstone/S. This is my report after spending an afternoon on the subject.
First, some links:
This blog is implemented in Pharo and is the natural choice for my Docker example application. I already have a Smalltalk snippet to load this blog's code and its dependencies into a pristine Pharo image, so I'll be using that. Also, as a matter of course, I build the Pharo VM from source, and my VM installation also contains self-built shared libraries like libsqlite.so and libshacrypt.so.
Outside of Docker, prepare a custom Pharo image:
% cp ../Pharo64-60543.image scms1.image
% cp ../Pharo64-60543.image scms1.image
% ~/pkg/pharo6vm64/gofaro scms1.image st loadSCMS1.st
gofaro is a simple shell script which purpose is to make sure the Pharo VM loads my custom shared libraries, co-located with the standard VM files, at run time:
#!/bin/sh
PHAROVMPATH=$(dirname `readlink -f "$0"`)
LD_LIBRARY_PATH="$PHAROVMPATH" exec "$PHAROVMPATH/pharo" $@
loadSCMS1.st looks like this:
"Load dependencies and then the blog code."
Gofer it ...
Gofer it ...
Metacello new ...
Metacello new ...
"Save the image for injection into Docker."
SmalltalkImage current snapshot: true andQuit: true
Before describing my Dockerfile, here are my conventions for inside the Docker container:
Starting with Ubuntu 18.04, install libfreetype6. The other lines are copied from Torsten's tutorial.
FROM ubuntu:18.04
LABEL maintainer="Pierce Ng"
RUN apt-get update \
&& apt-get -y install libfreetype6 \
&& apt-get -y upgrade \
&& rm -rf /var/lib/apt/lists/* \
&& true
Next, install the Pharo VM.
RUN mkdir -p /pkg/vm
COPY pharo6vm64/ /pkg/vm
COPY pharolimits.conf /etc/security/limits.d/pharo.conf
Now copy over the prepared Pharo image.
RUN mkdir -p /pkg/image
WORKDIR /pkg/image
COPY PharoV60.sources PharoV60.sources
COPY scms1.image scms1.image
COPY scms1.changes scms1.changes
COPY runSCMS1.st runSCMS1.st
Finally, set the Docker container running. Here we create a UID/GID pair to run the application. Said UID owns the mutable Pharo files in /pkg/image and also the /pkg/image directory itself, in case the application needs to create other files such as SQLite databases.
RUN groupadd -g 1099 pharoapp && useradd -r -u 1099 -g pharoapp pharoapp
RUN chown -R pharoapp:pharoapp /pkg/image
RUN chown root:root /pkg/image/PharoV60.sources
RUN chown root:root /pkg/image/runSCMS1.st
EXPOSE 8081
USER pharoapp:pharoapp
CMD /pkg/vm/gofaro -vm-display-null -vm-sound-null scms1.image --no-quit st runSCMS1.st
runSCMS1.st runs the blog application. In my current non-Dockerized installation, the runSCMS1.st-equivalent snippet is in a workspace; for Docker, to become DevOps/agile/CI/CD buzzwords-compliant, this snippet is run from the command line. This is one Dockerization adaptation I had to make to my application.
Now we build the Docker image.
% sudo docker build -t samadhiweb/scms1:monolithic .
Sending build context to Docker daemon 299MB
Step 1/19 : FROM ubuntu:18.04
---> cd6d8154f1e1
Step 2/19 : LABEL maintainer="Pierce Ng"
---> Using cache
---> 1defb3ac00a8
Step 3/19 : RUN apt-get update && apt-get -y install libfreetype6 && apt-get -y upgrade && rm -rf /var/lib/apt/lists/* && true
---> Running in b4e328138b50
<bunch of apt-get output>
Removing intermediate container b4e328138b50
---> 79e9d8ed7959
Step 4/19 : RUN mkdir -p /pkg/vm
---> Running in efb2b9b717fe
Removing intermediate container efb2b9b717fe
---> 0526cbc4c483
Step 5/19 : COPY pharo6vm64/ /pkg/vm
---> 2d751994c68c
Step 6/19 : COPY pharolimits.conf /etc/security/limits.d/pharo.conf
---> f442f475c568
Step 7/19 : RUN mkdir -p /pkg/image
---> Running in 143ebd54f243
Removing intermediate container 143ebd54f243
---> 6d1b99d30050
Step 8/19 : WORKDIR /pkg/image
---> Running in 45c76d8c08c0
Removing intermediate container 45c76d8c08c0
---> 57247408801b
Step 9/19 : COPY PharoV60.sources PharoV60.sources
---> 8802acc416f0
Step 10/19 : COPY scms1.image scms1.image
---> 3e2d62be5d00
Step 11/19 : COPY scms1.changes scms1.changes
---> dcbec7ebdda9
Step 12/19 : COPY runSCMS1.st runSCMS1.st
---> 72fa4efb33ff
Step 13/19 : RUN groupadd -g 1099 pharoapp && useradd -r -u 1099 -g pharoapp pharoapp
---> Running in e0af716c8db2
Removing intermediate container e0af716c8db2
---> 0a42beed8065
Step 14/19 : RUN chown -R pharoapp:pharoapp /pkg/image
---> Running in 2da21fefa399
Removing intermediate container 2da21fefa399
---> 0d808f48ae32
Step 15/19 : RUN chown root:root /pkg/image/PharoV60.sources
---> Running in 4ca0c6eb8301
Removing intermediate container 4ca0c6eb8301
---> 1426236b509c
Step 16/19 : RUN chown root:root /pkg/image/runSCMS1.st
---> Running in a942ecb8a155
Removing intermediate container a942ecb8a155
---> 1213e1647076
Step 17/19 : EXPOSE 8081
---> Running in 3b74e55b6394
Removing intermediate container 3b74e55b6394
---> a04593571d13
Step 18/19 : USER pharoapp:pharoapp
---> Running in 77ecde5a7ca7
Removing intermediate container 77ecde5a7ca7
---> 975b614d3a9f
Step 19/19 : CMD /pkg/vm/gofaro -vm-display-null -vm-sound-null scms1.image --no-quit st runSCMS1.st
---> Running in 2c6e7645da3d
Removing intermediate container 2c6e7645da3d
---> 65b4ca6cc5c5
Successfully built 65b4ca6cc5c5
Successfully tagged samadhiweb/scms1:monolithic
% sudo docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
samadhiweb/scms1 monolithic 65b4ca6cc5c5 2 minutes ago 402MB
...
%
The Docker image has been created, but it is not ready to run yet, because the web content is not in the image. I'll put the content in a Docker volume. Below, the first -v mounts my host's content directory into /tmp/webcontent in the container; the second -v mounts the volume smdw-content into /pkg/cms in the container; I'm running the busybox image to get a shell prompt; and within the container I copy the web content from the source to the destination.
% sudo docker volume create smdw-content
% sudo docker run --rm -it \
-v ~/work/webcms/samadhiweb:/tmp/webcontent \
-v smdw-content:/pkg/cms \
busybox sh
/ # cp -p -r /tmp/webcontent/* /pkg/cms/
/ # ^D
%
Finally, run the Docker image, taking care to mount the volume smdw-content, now with this blog's content:
% sudo docker run --rm -d -p 8081:8081 \
-v smdw-content:/pkg/cms \
--name samadhiweb samadhiweb/scms1:monolithic
bfcc80b32f35b3979c5c8c1b28bd3464f79ebdae91f51d9422334b209678ab5c
% sudo docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
bfcc80b32f35 samadhiweb/scms1:monolithic "/bin/sh -c '/pkg/vm." 4 seconds ago Up 3 seconds 0.0.0.0:8081->8081/tcp samadhiweb
Verified with a web browser. This works on my computer. :-)
Tags: deployment, Docker