How to automatically shutdown a GCE VM after the docker container finishes? - docker

I have a batch process (packaged in a docker image) that needs to run once a day somewhere in the cloud. Google Compute Engine (GCE) seems like a simple and easy to use option for this with it's container-optimized OS VM. This works really well, except that I cannot find an easy way to automatically shutdown the VM after the docker container finishes. You can specify a startup script but GCE seems to execute it before the container is started, so doing docker wait there does not work. I don't care that it's a docker-optimized VM. One of the other basic Linux OSs (like Debian) is fine as long as it can easily be setup with docker.
Is there an easy way of automatically shutting a Linux VM down after the docker container finishes?

As requested in the question comments, Python code to shutdown a Compute Engine instance.
Note: The Google Compute Engine Metadata server can provide the REPLACE variables in the script. Also, you do not need to wait for the results to be STOPPED, just verify that the script did not fail. You can test this program locally as well.
See this answer for tips on COS container credentials. The program below uses ADC (Application Default Credentials).
from pprint import pprint
import time
from googleapiclient import discovery
from oauth2client.client import GoogleCredentials
credentials = GoogleCredentials.get_application_default()
service = discovery.build('compute', 'v1', credentials=credentials)
# Project ID for this request.
project = 'REPLACE_WITH_PROJECT_ID'
# The name of the zone for this request.
zone = 'REPLACE_WITH_COMPUTE_ENGINE_ZONE'
# Name of the instance resource to start.
instance = 'REPLACE_WITH_COMPUTE_ENGINE_INSTANCE_NAME'
request = service.instances().stop(project=project, zone=zone, instance=instance)
response = request.execute()
pprint(response)
print('Waiting for operation to finish...')
print('Name:', response['name'])
while True:
result = service.zoneOperations().get(
project=project,
zone=zone,
operation=response['name']).execute()
print('status:', result['status'])
if result['status'] == 'DONE':
print("done.")
break;
if 'error' in result:
raise Exception(result['error'])
time.sleep(1)

I was able to avoid shutting down the VM from within the docker container by simply deploying a regular Debian VM on Compute Engine. Steps:
First, create a Debian VM on Compute Engine and pass a startup script that installs docker. Startup script:
#!/bin/bash
# Install docker. Taken from the docker documentation.
# Passed into the gcloud command that creates the VM instance.
apt-get -y remove docker docker-engine docker.io containerd runc
apt-get update
curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian \
$(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
apt-get update
apt-get -y install docker-ce docker-ce-cli containerd.io
echo Done installing docker.
Second, replace that startup script with one that pulls and runs the docker image:
#!/bin/bash
...
# Get authorization to pull from artifact registry
gcloud -q auth configure-docker us-east1-docker.pkg.dev
# Pull the image and run it, then shutdown.
docker pull $image
docker rm $container
docker run -v $vol_ini:$app_ini -v $vol_log:$app_log --name $container $image
shutdown now
Now this VM can be scheduled to run the docker image on a daily basis and automatically shut itself down.

Related

Start Node manager in Weblogic (Docker) using script.

I tried to dockerize weblogic server. Now I am facing a issue with Starting node manager after server is started inside the docker container. My docker file as below.
FROM oracle/weblogic:12.1.3-generic
ENV JAVA_OPTIONS="${JAVA_OPTIONS} -
Dweblogic.nodemanager.SecureListener=false" \
ADMIN_PORT="7001" \
ADMIN_HOST="localhost"
USER oracle
COPY dockerfiles/keyStore/keystore_ss.jks /u01/oracle/keystore/
COPY dockerfiles/patch/* /u01/oracle/patch/
COPY dockerfiles/local_domainScripts /u01/oracle/local_domainScripts/
COPY dockerfiles/scripts/* /u01/oracle/
COPY dockerfiles/applicationFiles/ /u01/oracle/applicationFiles/
USER root
RUN yum install -y procps
RUN chmod +x startWeblogic.sh
USER oracle
RUN /u01/oracle/wlst /u01/oracle/local_domainScripts/config.py
RUN nohup bash -c "/u01/oracle/user_projects/domains/local_domain/bin/startNodeManager.sh &" && sleep 4
CMD ["/u01/oracle/user_projects/domains/local_domain/startWebLogic.sh"]
This will create weblogic server instance. I want to start node manager after this server is started.
Run command:
docker run -d --name wls_local_domain --network=host --hostname localhost -p 7001:7001 test-docker:0-SNAPSHOT
When ./startNodeManager.sh is executed inside the container that will start the node manager. To start the node manager, weblogic server need to be started first.
I want to this using bash script. I tried this one but it didn't help
github link
You can't (usefully) RUN a background process. That Dockerfile command launches an intermediate container executing the RUN command, saves its filesystem, and exits; there is no process running any more by the time the next Dockerfile command executes.
If this is a commercially maintained image, you might look into whether Oracle has intstructions on how to use it. (From clicking around, none of the samples there start a node manager; is it necessary?)
Best practice is generally to run only one server in a Docker container (and ideally in the foreground and as the container's main process). If that will work and there aren't shared filesystem dependencies, you can split all of this except the final CMD into one base Dockerfile, then have two additional Dockerfiles that just have a FROM line pointing at your mostly-built image and a requested CMD.
If that really won't work then you'll have to fall back to running some init system in your container, typically supervisord.
You need to start the node manager as a background process then start the server. In order to keep alive the docker container while you are running the background processes, you can use the tail command.
This is how I start the node managed and the WebLogic server in my container:
#!/bin/bash
# ------------------------------------------------------------------------------
# start the Node Manager
# ------------------------------------------------------------------------------
function startNodeManager() {
echo "starting the node manager for $MANAGED_SERVER_NAME server..."
"$ORACLE_HOME/user_projects/domains/$DOMAIN_NAME/bin/startNodeManager.sh" &
while ! nc -z "$HOSTNAME" "$NODE_MANAGER_PORT"; do
sleep 0.5
done
echo "node manager is up and ready to receive requests"
}
# ------------------------------------------------------------------------------
# start the WebLogic Admin server
# ------------------------------------------------------------------------------
function startAdminServer() {
echo "starting the $ADMIN_SERVER_NAME server..."
local logHome
logHome="$ORACLE_HOME/user_projects/domains/$DOMAIN_NAME/servers/$ADMIN_SERVER_NAME/logs"
mkdir -p "$logHome"
"$ORACLE_HOME/user_projects/domains/$DOMAIN_NAME/bin/startWebLogic.sh" > "$logHome/$ADMIN_SERVER_NAME.out" 2>&1 &
}
# ------------------------------------------------------------------------------
# main app starts here
# ------------------------------------------------------------------------------
startNodeManager
startAdminServer
# this command keeps alive the docker container
tail -F \
"$ORACLE_HOME/user_projects/domains/$DOMAIN_NAME/servers/$ADMIN_SERVER_NAME/logs/$ADMIN_SERVER_NAME.log" \
"$ORACLE_HOME/user_projects/domains/$DOMAIN_NAME/servers/$ADMIN_SERVER_NAME/logs/$ADMIN_SERVER_NAME.nohup" \
"$ORACLE_HOME/user_projects/domains/$DOMAIN_NAME/servers/$ADMIN_SERVER_NAME/logs/$ADMIN_SERVER_NAME.out"
This is a complete startup script that you can use as an example and improve it. It starts the node manager and the admin server: https://github.com/zappee/docker-images/blob/master/oracle-weblogic/oracle-weblogic-12.2.1.4-admin-server/container-scripts/startup.sh
From here you can download the complete working solution.

Docker run desktop environment

The question is most clear,
How to start complete desktop environment (KDE, XFCE, Gnome doesn't matter) in the Docker remote container.
I were digging over the internet and there are lots of questions about the related topic, but not the same, they all about how to run GUI application not the full desktop.
What I found out:
Necessary run Xvfb
Somehow run e.g. Xfce in that FrameBuffer
Allow x11vnc to share that running X environment
But I'm stuck here actually, always getting whatever errors:
... (EE) Invalid screen configuration 1024x768 for -screen 0
... Cannot open /dev/tty0 (No such file or directory)
Could you give some Dockerfile lines in order reach the goal?
That is I was looking for, the simplest form of the desktop in Docker:
FROM ubuntu
RUN apt-get update
RUN apt-get install xfce4 -y
RUN apt-get install xfce4-goodies -y
RUN apt-get purge -y pm-utils xscreensaver*
RUN apt-get install wget -y
EXPOSE 5901
RUN wget -qO- https://dl.bintray.com/tigervnc/stable/tigervnc-1.8.0.x86_64.tar.gz | tar xz --strip 1 -C /
RUN mkdir ~/.vnc
RUN echo "123456" | vncpasswd -f >> ~/.vnc/passwd
RUN chmod 600 ~/.vnc/passwd
CMD ["/usr/bin/vncserver", "-fg"]
Unfortunately I could not sort out with x11vnc and xvfb. But TigerVNC turned out much better.
This sample generate container with xfce gui and run vncserver with 123456 password. There is no need to overwrite ~/.vnc/xstartup manually because TigerVNC starts up X server by default!
To run the server:
sudo docker run --rm -dti -p 5901:5901 3ab3e0e7cb
To connect there with vncviewer:
vncviewer -AutoSelect 0 -QualityLevel 9 -CompressLevel 0 192.168.1.100:5901
Also you could not care about screen resolution because by default it will resize to fit your screen:
You may also encounter the issue with ipc_channel_posix (chrome and other browsers will not work properly) to eliminate this run container with memory sharing:
docker run -d --shm-size=2g --privileged -p 5901:5901 image-name
x11docker allows to run desktop environments as well as single GUI applications in docker.
Could you give some Dockerfile lines in order reach the goal?
Example desktop images on docker hub.
x11docker does a lot of setup to keep container isolation and provides some additional options like hardware acceleration or pulseaudio sound. Example:
x11docker --desktop x11docker/lxde
x11docker also supports network setups with SSH, VNC and HTML5
Example for SSH setup with xpra:
read Xenv < <(x11docker --xdummy --display=30 x11docker/lxde pcmanfm)
echo $Xenv && export $Xenv
# replace "start" with "start-desktop" to forward a desktop environment
xpra start :30 --use-display --start-via-proxy=no
From client system, connect with
xpra attach ssh:HOSTNAME:30 # replace HOSTNAME with IP or host name of ssh server
Without x11docker:
A quite short setup using Xephyr as nested X server on host is:
Xephyr :1
docker run -v /tmp/.X11-unix/X1:/tmp/.X11-unix/X1:rw \
-e DISPLAY=:1 \
x11docker/xfce
A short Dockerfile with Xfce desktop:
FROM debian:stretch
ENV DEBIAN_FRONTEND noninteractive
RUN apt-get update && apt-get install -y --no-install-recommends xfce4 dbus-x11
CMD startxfce4

Installing packages into ubuntu14.04 docker container

I currently have similar images being built for virtualbox and digital ocean for dev and production (they're using packer and ansible to build). They're using Ubuntu 14.04.
I've created a docker version from the same scripts without any issue. This is going to be for a Gitlab CI environment.
When I come to install packages inside a container I get an error. Potentially to do with broken init systems? Something not running?
My initial command is /sbin/init and I've tried with and without phusion/base-image.
The error is msg: '/usr/bin/apt-get -y -o "Dpkg::Options::=--force-confdef" -o "Dpkg::Options::=--force-confold" install 'docker-engine'' failed: invoke-rc.d: unknown initscript, /etc/init.d/cgroup-lite not found.
dpkg: error processing package cgroup-lite (--configure):
(Yes, this is going to be a monolithic container rather than single-process and yes, I'm running docker from inside it - I'll be sharing docker.sock to make this work.)
So, I had a look at the code for invoke-rd.d and found this relevant snippet.
# If we're running on upstart and there's an upstart job of this name, do
# the rest with upstart instead of calling the init script.
if which initctl >/dev/null && initctl version | grep -q upstart \
&& [ -e "$UPSTARTDIR/${INITSCRIPTID}.conf" ]
then
is_upstart=1
elif test ! -f "${INITDPREFIX}${INITSCRIPTID}" ; then
## Verifies if the given initscript ID is known
## For sysvinit, this error is critical
printerror unknown initscript, ${INITDPREFIX}${INITSCRIPTID} not found.
if [ ! -e "$UPSTARTDIR/${INITSCRIPTID}.conf" ]; then
# If the init script doesn't exist, but the upstart job does, we
# defer the error exit; we might be running in a chroot and
# policy-rc.d might say not to start the job anyway, in which case
# we don't want to exit non-zero.
exit 100
fi
fi
A combination of docker replacing the init system, the inability to use upstart in an ubuntu docker container and the ubuntu package for cgroup-lite being built for upstart meant dpkg --configure was failing as the service couldn't be started.

How can I let the gitlab-ci-runner DinD image cache intermediate images?

I have a Dockerfile that starts with installing the texlive-full package, which is huge and takes a long time. If I docker build it locally, the intermedate image created after installation is cached, and subsequent builds are fast.
However, if I push to my own GitLab install and the GitLab-CI build runner starts, this always seems to start from scratch, redownloading the FROM image, and doing the apt-get install again. This seems like a huge waste to me, so I'm trying to figure out how to get the GitLab DinD image to cache the intermediate images between builds, without luck so far.
I have tried using the --cache-dir and --docker-cache-dir for the gitlab-runner register command, to no avail.
Is this even something the gitlab-runner DinD image is supposed to be able to do?
My .gitlab-ci.yml:
build_job:
script:
- docker build --tag=example/foo .
My Dockerfile:
FROM php:5.6-fpm
MAINTAINER Roel Harbers <roel.harbers#example.com>
RUN apt-get update && apt-get install -qq -y --fix-missing --no-install-recommends texlive-full
RUN echo Do other stuff that has to be done every build.
I use GitLab CE 8.4.0 and gitlab/gitlab-runner:latest as runner, started as
docker run -d --name gitlab-runner --restart always \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /usr/local/gitlab-ci-runner/config:/etc/gitlab-runner \
gitlab/gitlab-runner:latest \
; \
The runner is registered using:
docker exec -it gitlab-runner gitlab-runner register \
--name foo.example.com \
--url https://gitlab.example.com/ci \
--cache-dir /cache/build/ \
--executor docker \
--docker-image gitlab/dind:latest \
--docker-privileged \
--docker-disable-cache false \
--docker-cache-dir /cache/docker/ \
; \
This creates the following config.toml:
concurrent = 1
[[runners]]
name = "foo.example.com"
url = "https://gitlab.example.com/ci"
token = "foobarsldkflkdsjfkldsj"
tls-ca-file = ""
executor = "docker"
cache_dir = "/cache/build/"
[runners.docker]
image = "gitlab/dind:latest"
privileged = true
disable_cache = false
volumes = ["/cache"]
cache_dir = "/cache/docker/"
(I have experimented with different values for cache_dir, docker_cache_dir and disable_cache, all with the same result: no caching whatsoever)
I suppose there's no simple answer to your question. Before adding some details, I strongly suggest to read this blog article from the maintainer of DinD, which was originally named "do not use Docker in Docker for CI".
What you might try is declaring /var/lib/docker as a volume for your GitLab runner. But be warned, depending on your file-system drivers you may use AUFS in the container on an AUFS filesystem on your host, which is very likely to cause problems.
What I'd suggest to you is creating a separate Docker-VM, only for the runner(s), and bind-mount docker.sock from the VM into your runner-container.
We are using this setup with GitLab with great success (>27.000 builds in about 12 months).
You can take a look at our runner with docker-compose support which is actually based on the shell-executor of GitLab's runner.
Currently you cannot cache intermediate layers in GitLab Docker-in-Docker. Altough there are plans to add that (that are mentioned in the link below). What you can do today to speed up your DinD build is to use the overlay filesystem. To do this you need to be running a liunx kernel >=3.18 and make sure you load the overlay kernel module. Then you set this variable in your gitlab-ci.yml:
variables:
DOCKER_DRIVER: overlay
For more information see this issue and in particular this comment on "The state of optimising Docker Builds!", see the "Using docker executor with dind" section.
https://gitlab.com/gitlab-org/gitlab-ce/issues/17861#note_12991518
For build dependencies that do not change so ofter you can do kinda manual caching with gitlab image registry.
In CI script you do not explicitely call docker build but rather wrap it in a shell script
# cat build_dependencies.sh
registry=registry.example.com
project=group/project
imagebase=$registry/$project/linux
docker pull $imagebase/devbase:1.0
if [ $? -ne 0 ]; then
docker build -f devbase.dockerfile -t $imagebase/devbase:1.0 .
docker push $imagebase/devbase:1.0
fi
...
and call that script in your CI
...
script:
- ./build_dependencies.sh
The downside to this is that when your devbase.dockerfile is updated this would get unnoticed by CI, so you need to force build and push of a new image. So for dynamicly changing images this does not work well, but for your use case this seems like a possible way to go.

How to test the container or image after docker build?

I have the following Dockerfile
############################################################
# Purpose : Dockerize Django App to be used in AWS EC2
# Django : 1.8.1
# OS : Ubuntu 14.04
# WebServer : nginx
# Database : Postgres inside RDS
# Python : 2.7
# VERSION : 0.1
############################################################
from ubuntu:14.04
maintainer Kim Stacks, kimcity#gmail.com
# make sure package repository is up to date
run echo "deb http://archive.ubuntu.com/ubuntu $(lsb_release -sc) main universe" > /etc/apt/sources.list
run apt-get update
# install python
# install nginx
Inside my VM, I did the following:
docker build -t ubuntu1404/djangoapp .
It is successful.
What do I do to run the docker image?
Where is the image or container?
I have already tried running
docker run ubuntu1404/djangoapp
Nothing happens.
What I see when I run docker images
root#vagrant-ubuntu-trusty-64:/var/virtual/Apps/DockerFiles/Django27InUbuntu# docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
ubuntu1404/djangoapp latest cfb161605c8e 10 minutes ago 198.3 MB
ubuntu 14.04 07f8e8c5e660 10 days ago 188.3 MB
hello-world latest 91c95931e552 3 weeks ago 910 B
When I run docker ps, nothing shows up
You have to give a command your container will have to process.
Example : sh
you could try :
docker run -ti yourimage sh
(-ti is used to keep a terminal open)
If you want to launch a daemon (like a server), you will have to enter something like :
docker run -d yourimage daemontolaunch
Use docker help run for more options.
You also can set a default behaviour with CMD instruction in your Dockerfile so you won't have to give this command to your container each time you want to run it.
EDIT - about container removing :
Containers and images are different.
A container is an instance of an image.
You can run several containers from the same image.
The container automatically stops when the process it runs terminates.
But the container isn't deleted (just stopped, so you can restart it).
But if you want to remove it (removing a container doesn't remove the image) you have two ways to do :
automatically removing it at the end of the process by adding --rm option to docker run.
Manually removing it by using the docker rm command and giving it the container ID or its name (a container has to be stopped before being removed, use docker stop for this).
A usefull command :
Use docker ps to list containers. -q to display only the container IDs, -a to display even stopped containers.
More here.
EDIT 2:
This could also help you to discover docker if you didn't try it.
How to test the container or image after docker build?
In order to test you can add write a bash script which will do the job https://blog.brazdeikis.io/posts/docker-image-tests
Btw, from the post, I see that it does not match the question from the title.
So, Added a link for the souls who arrived here based on the title...
Download the latest shaded dist from https://github.com/dgroup/docker-unittests/releases:
wget https://github.com/dgroup/docker-unittests/releases/download/s1.1.1/docker-unittests-app-1.1.1.jar
De fine an *.yml file with tests.
version: 1.1
setup:
- apt-get update
- apt-get install -y tree
tests:
- assume: java version is 1.9, Debian build
cmd: java -version
output:
contains:
- openjdk version "9.0.1"
- build 9.0.1+11-Debian
- assume: curl version is 7.xxx
cmd: curl --version
output:
startsWith: curl 7.
matches:
- "^curl\\s7.*\\n.*\\nProtocols.+ftps.+https.+telnet.*\\n.*\\n$"
contains:
- AsynchDNS IDN IPv6 Largefile GSS-API
- assume: Setup section installed `tree`
cmd: tree --version
output:
contains: ["Steve Baker", "Florian Sesser"]
Run tests for image
java -jar docker-unittests.jar -f image-tests.yml -i openjdk:9.0.1-11
https://i.stack.imgur.com/DSv72.png

Resources