mirror of
https://github.com/vacp2p/linea-besu.git
synced 2026-01-09 15:37:54 -05:00
Make a new Kubernetes friendly Docker image (#1499)
The following PIE-1598 subtasks are fixed: fixes PIE-1600 remove entrypoint script fixes PIE-1601 investigate Dockerfile best practices including : - naming - multi step build - comments - labels - build args - entrypoint - minimalism fixes PIE-1604 rewrite Dockerfile according to best practices provide a sample build command that can be used as-is or as an example. Added contents to .dockerignore to make the intermediate build image smaller. Remove .env file that was supposed to be used long ago for a docker quickstart and that we forgot to remove ans is useless today. add jenkins pipeline to test the docker image build Signed-off-by: Adrian Sutton <adrian.sutton@consensys.net>
This commit is contained in:
@@ -1,4 +1,15 @@
|
||||
.github
|
||||
.gradle
|
||||
.idea
|
||||
.vertx
|
||||
build
|
||||
build
|
||||
kubernetes
|
||||
Dockerfile
|
||||
|
||||
#Exclude doc related resources
|
||||
docs
|
||||
mkdocs.yml
|
||||
readthedocs.yml
|
||||
*.md
|
||||
|
||||
Jenkins*
|
||||
5
.env
5
.env
@@ -1,5 +0,0 @@
|
||||
#Default env variables for docker compose quickstart
|
||||
#defaults are empty values
|
||||
RPC_PORT_MAPPING=
|
||||
WS_PORT_MAPPING=
|
||||
EXPLORER_PORT_MAPPING=
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -26,3 +26,5 @@ tmp/
|
||||
build/
|
||||
out/
|
||||
site/
|
||||
/kubernetes/reports/
|
||||
/kubernetes/pantheon-*.tar.gz
|
||||
|
||||
90
Jenkinsfile
vendored
90
Jenkinsfile
vendored
@@ -174,29 +174,85 @@ try {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (env.BRANCH_NAME == "master") {
|
||||
node {
|
||||
checkout scm
|
||||
unstash 'distTarBall'
|
||||
docker.image(docker_image_dind).withRun('--privileged') { d ->
|
||||
docker.image(docker_image).inside("-e DOCKER_HOST=tcp://docker:2375 --link ${d.id}:docker") {
|
||||
stage('build image') {
|
||||
sh "cd docker && cp ../build/distributions/pantheon-*.tar.gz ."
|
||||
pantheon = docker.build("pegasyseng/pantheon:develop", "docker")
|
||||
def registry = 'https://registry.hub.docker.com'
|
||||
def userAccount = 'dockerhub-pegasysengci'
|
||||
def imageRepos = 'pegasyseng'
|
||||
def imageTag = 'develop'
|
||||
parallel KubernetesDockerImage: {
|
||||
def stage_name = 'Kubernetes Docker image node: '
|
||||
def image = imageRepos + '/pantheon-kubernetes:' + imageTag
|
||||
def kubernetes_folder = 'kubernetes'
|
||||
def kubernetes_image_build_script = kubernetes_folder + '/build_image.sh'
|
||||
def version_property_file = 'gradle.properties'
|
||||
def reports_folder = kubernetes_folder + '/reports'
|
||||
def dockerfile = kubernetes_folder + '/Dockerfile'
|
||||
node {
|
||||
checkout scm
|
||||
unstash 'distTarBall'
|
||||
docker.image(build_image).inside() {
|
||||
stage(stage_name + 'Dockerfile lint') {
|
||||
sh "docker run --rm -i hadolint/hadolint < ${dockerfile}"
|
||||
}
|
||||
stage(stage_name + 'Build image') {
|
||||
sh "${kubernetes_image_build_script} '${image}'"
|
||||
}
|
||||
stage(stage_name + "Test image labels") {
|
||||
shortCommit = sh(returnStdout: true, script: "git log -n 1 --pretty=format:'%h'").trim()
|
||||
version = sh(returnStdout: true, script: "grep -oE \"version=(.*)\" ${version_property_file} | cut -d= -f2").trim()
|
||||
sh "docker image inspect \
|
||||
--format='{{index .Config.Labels \"org.label-schema.vcs-ref\"}}' \
|
||||
${image} \
|
||||
| grep ${shortCommit}"
|
||||
sh "docker image inspect \
|
||||
--format='{{index .Config.Labels \"org.label-schema.version\"}}' \
|
||||
${image} \
|
||||
| grep ${version}"
|
||||
}
|
||||
try {
|
||||
stage('test image') {
|
||||
sh "apk add bash"
|
||||
sh "mkdir -p docker/reports"
|
||||
sh "cd docker && bash test.sh pegasyseng/pantheon:develop"
|
||||
stage(stage_name + 'Test image') {
|
||||
sh "mkdir -p ${reports_folder}"
|
||||
sh "cd ${kubernetes_folder} && bash test.sh ${image}"
|
||||
}
|
||||
} finally {
|
||||
junit 'docker/reports/*.xml'
|
||||
sh "rm -rf docker/reports"
|
||||
junit "${reports_folder}/*.xml"
|
||||
sh "rm -rf ${reports_folder}"
|
||||
}
|
||||
stage('push image') {
|
||||
docker.withRegistry('https://registry.hub.docker.com', 'dockerhub-pegasysengci') {
|
||||
pantheon.push()
|
||||
stage(stage_name + 'Push image') {
|
||||
docker.withRegistry(registry, userAccount) {
|
||||
docker.image(image).push()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
DockerImage: {
|
||||
def stage_name = 'Docker image node: '
|
||||
def image = imageRepos + '/pantheon:' + imageTag
|
||||
node {
|
||||
checkout scm
|
||||
unstash 'distTarBall'
|
||||
docker.image(docker_image_dind).withRun('--privileged') { d ->
|
||||
docker.image(docker_image).inside("-e DOCKER_HOST=tcp://docker:2375 --link ${d.id}:docker") {
|
||||
stage(stage_name + 'build image') {
|
||||
sh "cd docker && cp ../build/distributions/pantheon-*.tar.gz ."
|
||||
pantheon = docker.build(image, "docker")
|
||||
}
|
||||
try {
|
||||
stage('test image') {
|
||||
sh "apk add bash"
|
||||
sh "mkdir -p docker/reports"
|
||||
sh "cd docker && bash test.sh ${image}"
|
||||
}
|
||||
} finally {
|
||||
junit 'docker/reports/*.xml'
|
||||
sh "rm -rf docker/reports"
|
||||
}
|
||||
stage(stage_name + 'push image') {
|
||||
docker.withRegistry(registry, userAccount) {
|
||||
pantheon.push()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
36
kubernetes/Dockerfile
Normal file
36
kubernetes/Dockerfile
Normal file
@@ -0,0 +1,36 @@
|
||||
# extract image stage
|
||||
# extractin here reduces the number of layers in the final image
|
||||
FROM alpine:3.9 AS extract-stage
|
||||
# Copy Pantheon binaries from previous jenkins artefact step
|
||||
# or from the result of ./gradlew distTar
|
||||
# and lett ADD unpack them
|
||||
ADD pantheon-*.tar.gz /tmp/
|
||||
|
||||
# Run image stage
|
||||
# Use openJDK JRE only for running pantheon
|
||||
FROM openjdk:11.0.2-jre-slim-stretch
|
||||
# Copy extracted binaries from the previous step image
|
||||
COPY --from=extract-stage /tmp/pantheon* /opt/pantheon
|
||||
WORKDIR /opt/pantheon
|
||||
# Expose services ports
|
||||
# 8545 HTTP JSON-RPC
|
||||
# 8546 WS JSON-RPC
|
||||
# 8547 HTTP GraphQL
|
||||
# 30303 P2P
|
||||
EXPOSE 8545 8546 8547 30303
|
||||
ENTRYPOINT ["/opt/pantheon/bin/pantheon"]
|
||||
# Build-time metadata as defined at http://label-schema.org
|
||||
# Use the build_image.sh script in the kubernetes directory of this project to
|
||||
# easily build this image or as an example of how to inject build parameters.
|
||||
ARG BUILD_DATE
|
||||
ARG VCS_REF
|
||||
ARG VERSION
|
||||
LABEL org.label-schema.build-date=$BUILD_DATE \
|
||||
org.label-schema.name="Pantheon" \
|
||||
org.label-schema.description="Enterprise Ethereum client" \
|
||||
org.label-schema.url="https://docs.pantheon.pegasys.tech/" \
|
||||
org.label-schema.vcs-ref=$VCS_REF \
|
||||
org.label-schema.vcs-url="https://github.com/PegaSysEng/pantheon.git" \
|
||||
org.label-schema.vendor="Pegasys" \
|
||||
org.label-schema.version=$VERSION \
|
||||
org.label-schema.schema-version="1.0"
|
||||
39
kubernetes/build_image.sh
Executable file
39
kubernetes/build_image.sh
Executable file
@@ -0,0 +1,39 @@
|
||||
#!/bin/sh -e
|
||||
# This script presents a sample way to build Pantheon Docker image
|
||||
# with automatic build arguments from the current build workspace.
|
||||
# It must be started from the same path as where the Dockerfile is located.
|
||||
# you have to pass the imnage tag as an argument like for instance :
|
||||
# build_image.sh "pegasyseng/pantheon-kubernetes:develop"
|
||||
|
||||
CONTEXT_FOLDER=kubernetes/
|
||||
PANTHEON_BUILD_SOURCE='build/distributions/pantheon-*.tar.gz'
|
||||
|
||||
# Checking that you passed the tag for the image to be build
|
||||
if [ -z "$1" ]
|
||||
then
|
||||
me=`basename "$0"`
|
||||
echo "No image tag argument supplied to ${me}"
|
||||
echo "ex.: ${me} \"pegasyseng/pantheon-kubernetes:develop\""
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# looking for the distribution archive, either form CI step that builds form this
|
||||
# workspace sources but with multiple test steps first
|
||||
# or it builds it if you don't have one as you are probably
|
||||
# not in a CI step.
|
||||
if ls ${PANTHEON_BUILD_SOURCE} 1> /dev/null 2>&1; then
|
||||
cp ${PANTHEON_BUILD_SOURCE} ${CONTEXT_FOLDER}
|
||||
else
|
||||
echo "No pantheon-*.tar.gz archive found."
|
||||
echo "You are probably not running this from CI so running './gradlew distTar' first to have a local build"
|
||||
./gradlew distTar
|
||||
cp ${PANTHEON_BUILD_SOURCE} ${CONTEXT_FOLDER}
|
||||
fi
|
||||
|
||||
# Builds docker image with tags matching the info form this current workspace
|
||||
docker build \
|
||||
-t "$1" \
|
||||
--build-arg BUILD_DATE="`date`" \
|
||||
--build-arg VCS_REF="`git show -s --format=%h`" \
|
||||
--build-arg VERSION="`grep -oE "version=(.*)" gradle.properties | cut -d= -f2`" \
|
||||
${CONTEXT_FOLDER}
|
||||
24
kubernetes/test.sh
Executable file
24
kubernetes/test.sh
Executable file
@@ -0,0 +1,24 @@
|
||||
#!/bin/bash
|
||||
|
||||
export GOSS_PATH=tests/goss-linux-amd64
|
||||
export GOSS_OPTS="$GOSS_OPTS --format junit"
|
||||
export GOSS_FILES_STRATEGY=cp
|
||||
DOCKER_IMAGE=$1
|
||||
|
||||
i=0
|
||||
|
||||
# Test for normal startup with ports opened
|
||||
GOSS_FILES_PATH=tests/01 \
|
||||
bash tests/dgoss \
|
||||
run $DOCKER_IMAGE \
|
||||
--network=dev \
|
||||
--p2p-host=0.0.0.0 \
|
||||
--rpc-http-enabled \
|
||||
--rpc-http-host=0.0.0.0 \
|
||||
--rpc-ws-enabled \
|
||||
--rpc-ws-host=0.0.0.0 \
|
||||
--graphql-http-enabled \
|
||||
--graphql-http-host=0.0.0.0 \
|
||||
> ./reports/01.xml || i=`expr $i + 1`
|
||||
|
||||
exit $i
|
||||
40
kubernetes/tests/01/goss.yaml
Normal file
40
kubernetes/tests/01/goss.yaml
Normal file
@@ -0,0 +1,40 @@
|
||||
file:
|
||||
/opt/pantheon/bin/pantheon:
|
||||
exists: true
|
||||
mode: "0755"
|
||||
owner: root
|
||||
group: root
|
||||
filetype: file
|
||||
contains: []
|
||||
/opt/pantheon/database:
|
||||
exists: true
|
||||
mode: "0755"
|
||||
owner: root
|
||||
group: root
|
||||
filetype: directory
|
||||
contains: []
|
||||
/opt/pantheon/key:
|
||||
exists: true
|
||||
mode: "0600"
|
||||
owner: root
|
||||
group: root
|
||||
filetype: file
|
||||
contains: []
|
||||
port:
|
||||
tcp:8545:
|
||||
listening: true
|
||||
tcp:8546:
|
||||
listening: true
|
||||
tcp:8547:
|
||||
listening: true
|
||||
tcp:30303:
|
||||
listening: true
|
||||
ip:
|
||||
- 0.0.0.0
|
||||
udp:30303:
|
||||
listening: true
|
||||
ip:
|
||||
- 0.0.0.0
|
||||
process:
|
||||
java:
|
||||
running: true
|
||||
17
kubernetes/tests/01/goss_wait.yaml
Normal file
17
kubernetes/tests/01/goss_wait.yaml
Normal file
@@ -0,0 +1,17 @@
|
||||
port:
|
||||
tcp:30303:
|
||||
listening: true
|
||||
ip:
|
||||
- 0.0.0.0
|
||||
tcp:8545:
|
||||
listening: true
|
||||
ip:
|
||||
- 0.0.0.0
|
||||
tcp:8546:
|
||||
listening: true
|
||||
ip:
|
||||
- 0.0.0.0
|
||||
tcp:8547:
|
||||
listening: true
|
||||
ip:
|
||||
- 0.0.0.0
|
||||
113
kubernetes/tests/dgoss
Executable file
113
kubernetes/tests/dgoss
Executable file
@@ -0,0 +1,113 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
USAGE="USAGE: $(basename "$0") [run|edit] <docker_run_params>"
|
||||
GOSS_FILES_PATH="${GOSS_FILES_PATH:-.}"
|
||||
|
||||
info() {
|
||||
echo -e "INFO: $*" >&2;
|
||||
}
|
||||
error() {
|
||||
echo -e "ERROR: $*" >&2;
|
||||
exit 1;
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
set +e
|
||||
{ kill "$log_pid" && wait "$log_pid"; } 2> /dev/null
|
||||
rm -rf "$tmp_dir"
|
||||
if [[ $id ]];then
|
||||
info "Deleting container"
|
||||
docker rm -vf "$id" > /dev/null
|
||||
fi
|
||||
}
|
||||
|
||||
run(){
|
||||
# Copy in goss
|
||||
cp "${GOSS_PATH}" "$tmp_dir/goss"
|
||||
chmod 755 "$tmp_dir/goss"
|
||||
[[ -e "${GOSS_FILES_PATH}/goss.yaml" ]] && cp "${GOSS_FILES_PATH}/goss.yaml" "$tmp_dir"
|
||||
[[ -e "${GOSS_FILES_PATH}/goss_wait.yaml" ]] && cp "${GOSS_FILES_PATH}/goss_wait.yaml" "$tmp_dir"
|
||||
[[ ! -z "${GOSS_VARS}" ]] && [[ -e "${GOSS_FILES_PATH}/${GOSS_VARS}" ]] && cp "${GOSS_FILES_PATH}/${GOSS_VARS}" "$tmp_dir"
|
||||
|
||||
# Switch between mount or cp files strategy
|
||||
GOSS_FILES_STRATEGY=${GOSS_FILES_STRATEGY:="mount"}
|
||||
case "$GOSS_FILES_STRATEGY" in
|
||||
mount)
|
||||
info "Starting docker container"
|
||||
id=$(docker run -d -v "$tmp_dir:/goss:z" "${@:2}")
|
||||
docker logs -f "$id" > "$tmp_dir/docker_output.log" 2>&1 &
|
||||
;;
|
||||
cp)
|
||||
info "Creating docker container"
|
||||
id=$(docker create ${@:2})
|
||||
info "Copy goss files into container"
|
||||
docker cp $tmp_dir/. $id:/goss
|
||||
info "Starting docker container"
|
||||
docker start $id > /dev/null
|
||||
;;
|
||||
*) error "Wrong goss files strategy used! Correct options are \"mount\" or \"cp\"."
|
||||
esac
|
||||
|
||||
log_pid=$!
|
||||
info "Container ID: ${id:0:8}"
|
||||
}
|
||||
|
||||
get_docker_file() {
|
||||
if docker exec "$id" sh -c "test -e $1" > /dev/null;then
|
||||
mkdir -p "${GOSS_FILES_PATH}"
|
||||
info "Copied '$1' from container to '${GOSS_FILES_PATH}'"
|
||||
docker cp "$id:$1" "${GOSS_FILES_PATH}"
|
||||
fi
|
||||
}
|
||||
|
||||
# Main
|
||||
tmp_dir=$(mktemp -d /tmp/tmp.XXXXXXXXXX)
|
||||
chmod 777 "$tmp_dir"
|
||||
trap 'ret=$?;cleanup;exit $ret' EXIT
|
||||
|
||||
GOSS_PATH="${GOSS_PATH:-$(which goss 2> /dev/null || true)}"
|
||||
[[ $GOSS_PATH ]] || { error "Couldn't find goss installation, please set GOSS_PATH to it"; }
|
||||
[[ ${GOSS_OPTS+x} ]] || GOSS_OPTS="--color --format documentation"
|
||||
[[ ${GOSS_WAIT_OPTS+x} ]] || GOSS_WAIT_OPTS="-r 30s -s 1s > /dev/null"
|
||||
GOSS_SLEEP=${GOSS_SLEEP:-0.2}
|
||||
|
||||
case "$1" in
|
||||
run)
|
||||
run "$@"
|
||||
if [[ -e "${GOSS_FILES_PATH}/goss_wait.yaml" ]]; then
|
||||
info "Found goss_wait.yaml, waiting for it to pass before running tests"
|
||||
if [[ -z "${GOSS_VARS}" ]]; then
|
||||
if ! docker exec "$id" sh -c "/goss/goss -g /goss/goss_wait.yaml validate $GOSS_WAIT_OPTS"; then
|
||||
error "goss_wait.yaml never passed"
|
||||
fi
|
||||
else
|
||||
if ! docker exec "$id" sh -c "/goss/goss -g /goss/goss_wait.yaml --vars='/goss/${GOSS_VARS}' validate $GOSS_WAIT_OPTS"; then
|
||||
error "goss_wait.yaml never passed"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
[[ $GOSS_SLEEP ]] && { info "Sleeping for $GOSS_SLEEP"; sleep "$GOSS_SLEEP"; }
|
||||
# info "Container health"
|
||||
# if ! docker top $id; then
|
||||
# docker logs $id
|
||||
# fi
|
||||
info "Running Tests"
|
||||
if [[ -z "${GOSS_VARS}" ]]; then
|
||||
docker exec "$id" sh -c "/goss/goss -g /goss/goss.yaml validate $GOSS_OPTS"
|
||||
else
|
||||
docker exec "$id" sh -c "/goss/goss -g /goss/goss.yaml --vars='/goss/${GOSS_VARS}' validate $GOSS_OPTS"
|
||||
fi
|
||||
;;
|
||||
edit)
|
||||
run "$@"
|
||||
info "Run goss add/autoadd to add resources"
|
||||
docker exec -it "$id" sh -c 'cd /goss; PATH="/goss:$PATH" exec sh'
|
||||
get_docker_file "/goss/goss.yaml"
|
||||
get_docker_file "/goss/goss_wait.yaml"
|
||||
[[ ! -z "${GOSS_VARS}" ]] && get_docker_file "/goss/${GOSS_VARS}"
|
||||
;;
|
||||
*)
|
||||
error "$USAGE"
|
||||
esac
|
||||
BIN
kubernetes/tests/goss-linux-amd64
Normal file
BIN
kubernetes/tests/goss-linux-amd64
Normal file
Binary file not shown.
Reference in New Issue
Block a user