Skip to content

Instantly share code, notes, and snippets.

@drmalex07
Last active September 11, 2024 01:03
Show Gist options
  • Select an option

  • Save drmalex07/669d7b15b0df33e249a2 to your computer and use it in GitHub Desktop.

Select an option

Save drmalex07/669d7b15b0df33e249a2 to your computer and use it in GitHub Desktop.
An example docker entrypoint. #docker #docker-entrypoint #pid-1

README

A docker entrypoint must ensure that after any initialization (environmanet setup etc.) the main docker process must remain at PID 1. This is usually accomplished by using exec as the last command (which actually replaces the current process).

See also: https://docs.docker.com/engine/reference/builder/#entrypoint

An example entrypoint.sh:

#!/bin/bash

# Trigger an error if non-zero exit code is encountered
set -e 

# Modify the enviroment
export FOO="Baz"

# Decide what is to replace this process
if [ "${1}" == "serve" ]; then
    # This is the primary target 
    shift # consume the 1st argument
    exec /usr/bin/env python /path/to/server.py ${@}
else
    # An unknown command (debugging the container?): Forward as is
    exec ${@}
fi

An example server.py:

import os
import sys
import logging
import signal
import urllib2
from multiprocessing import Process
from time import sleep
from flask import Flask, request

prog_name = sys.argv[0]

logging.basicConfig(level=logging.INFO)

# Dump environment

print "%s: Args: %r" % (prog_name, sys.argv[1:])
print "%s: Environment: " % (prog_name)
for k, v in os.environ.items(): 
    print ' - ', k, v

# Handle signals in parent process

def sigterm_handler(signum, frame):
    logging.warn(' ** Caught SIGTERM (%s) at PID %s: Trigger shutdown ...' %(
        signum, os.getpid()))
    r = urllib2.urlopen('http://localhost:5001/_shutdown')

signal.signal(signal.SIGTERM, sigterm_handler)

# Start server in a dedicated worker process

app = Flask(__name__)

@app.route('/')
def index():
    logging.info('[client:%s] Working ...' % (request.remote_addr))
    sleep(12)
    logging.info('[client:%s] Done' % (request.remote_addr))
    return 'Hello World'

@app.route('/_shutdown')
def shutdown():
    if request.remote_addr == '127.0.0.1':
        logging.info('Received shutdown request...')
        request.environ.get('werkzeug.server.shutdown')()
        return 'OK'

def start_server():
    logging.info('Starting server worker at PID %s' % (os.getpid()))
    app.run(host='0.0.0.0', port=5001)

server = Process(target=start_server, name='flask worker', args=())
server.start()
logging.info('Server started with manager at PID %s' % (os.getpid()))

server.join()
logging.info('Server was shutdown')

An example Dockerfile:

FROM python:2.7

ENV EXAMPLE_ENTRYPOINT=1

RUN pip install flask 
ADD serve.py /opt/serve.py
ADD entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh

EXPOSE 5001

ENTRYPOINT ["/entrypoint.sh"]
CMD ["serve"]
@alexandrubau
Copy link
Copy Markdown

Hi @drmalex07! Your entrypoint.sh example was of great help to me. One suggestion though, I think you should use double quotes when forwarding the command. Example: "${@}" instead of ${@}. I encountered this issue inside a Gitlab Runner. You can find more information here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment