Skip to content
Snippets Groups Projects
Commit b7137545 authored by Ulf Seltmann's avatar Ulf Seltmann
Browse files

initial commit

parents
Branches
Tags
No related merge requests found
Pipeline #557 passed with stages
in 1 minute and 38 seconds
*
!assets
\ No newline at end of file
/.tmp
\ No newline at end of file
stages:
- build
- publish
- deploy
- docs
- mirror
variables:
image_name: ubleipzig/deployer
docker_build:
stage: build
image: docker:latest
services:
- docker:dind
script: |
docker build --pull \
--build-arg HTTP_PROXY=${HTTP_PROXY} \
--build-arg HTTPS_PROXY=${HTTPS_PROXY} \
--build-arg NO_PROXY=${NO_PROXY} \
--build-arg http_proxy=${HTTP_PROXY} \
--build-arg https_proxy=${HTTPS_PROXY} \
--build-arg no_proxy=${NO_PROXY} \
-t image \
.
docker save --output=image.tar.gz image
artifacts:
name: docker-image
paths:
- image.tar.gz
tags:
- docker
docker_publish_production:
stage: publish
image: docker:latest
services:
- docker:dind
script: |
mkdir -p ~/.docker && echo "$DOCKER_AUTH_CONFIG" >~/.docker/config.json
version=`expr ${CI_COMMIT_TAG} ':' 'release/\(.\+\)'`
major_version=`expr ${version} ':' '\([^.]\+\)'`
minor_version=`expr ${version} ':' '[^.]\+\.\([^.]\+\)'`
patch_version=`expr ${version} ':' '[^.]\+\.[^.]\+\.\(.\+\)'`
docker load --input=image.tar.gz
docker tag image ${image_name}:${version}
docker push ${image_name}:${version}
for tag in "latest" "${major_version}" "${major_version}.${minor_version}"; do
docker tag ${image_name}:${version} ${image_name}:${tag}
docker push ${image_name}:${tag}
done
dependencies:
- docker_build
tags:
- docker
except:
- branches
only:
- /^release\/.*/
docker_publish_alpha:
stage: publish
image: docker:latest
services:
- docker:dind
script: |
test "${DOCKER_AUTH_CONFIG}" == "" && echo "docker-config does not exists, aborting!" && false
mkdir -p ~/.docker && echo "$DOCKER_AUTH_CONFIG" >~/.docker/config.json
docker load --input=image.tar.gz
docker tag image ${image_name}:alpha-${CI_COMMIT_REF_NAME}
docker push ${image_name}:alpha-${CI_COMMIT_REF_NAME}
dependencies:
- docker_build
tags:
- docker
only:
- /^[0-9]+-/
docker_publish_staging:
stage: publish
image: docker:latest
services:
- docker:dind
script: |
mkdir -p ~/.docker && echo "$DOCKER_AUTH_CONFIG" >~/.docker/config.json
docker load --input=image.tar.gz
docker tag image ${image_name}:staging
docker push ${image_name}:staging
dependencies:
- docker_build
tags:
- docker
only:
- master
FROM docker:latest
ENTRYPOINT [ "/usr/local/bin/deployer" ]
CMD [ "help" ]
COPY assets/deployer /usr/local/bin/
WORKDIR /app
RUN apk add --no-cache bash curl \
&& curl -L https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl >/usr/local/bin/kubectl \
&& curl -L https://storage.googleapis.com/kubernetes-helm/helm-v2.9.1-linux-amd64.tar.gz | tar -zxf - -C /tmp linux-amd64/helm -O >/usr/local/bin/helm \
&& chmod a+x /usr/local/bin/deployer /usr/local/bin/kubectl /usr/local/bin/helm \
&& adduser -DS deployer
USER deployer
\ No newline at end of file
# deployer
A tool simplifying image building and deploying utilising docker, kubectl and helm.
The tool tailors the commands according to the [Workflow of University Library of Leipzig] for creating official docker images and deploying to alpha- staging- and production environments.
# Usage
The tool is bundled with docker, kubectl and helm into a docker image itself. You can use it like this:
```
docker run --rm --volume $PWD:/app ubleipzig/deployer:latest build --build-arg http_proxy=http://proxy.example.com:3128 --output image.tar.gz
```
_the working directory inside the container is `/app`, so make sure to bind local files there._
## deployer build
This command builds an image from local context. You can set multiple `--build-arg` options mainly used for providing proxy-environment variables such as `HTTP_PROXY`, `http_proxy`, ...
The
See [Advanced Configuration] for more information.
```
$ deployer build --build-arg http_proxy=http://proxy.example.com:3128 --output image.tar.gz
```
_builds image and saves it to file `image.tar.gz`_
## deployer publish
This command publishes an image provided as `tar.gz`-file to [Docker-Hub]. The credentials are provided as file content of dockers config-file located by default under `~/.docker/config.json`
```
$ deployer publish --docker-config "$(cat ~/.docker/config.json)" --input image.tar.gz --name example/image --tag latest --tag 1.0 --tag 1
```
_publishes the image from `image.tar.gz` to [Docker-Hub] as *example/image:latest*, *example/image:1.0* and *example/image:1*_
## deployer deploy
This command deploys a helm-chart to a kubernetes cluster. The credentials are provided by the cluster-admin as well as the namespace and the service-account.
```
$ deployer deploy \
--namespace example_namespace \
--cluster-url https://k8s-cluster.example.com:6443 \
--certificate-authority "$base64_encoded_cacert" \
--token "$base64_encoded_bearer_token" \
--name example-staging \
--charts ./helmcharts \
--service-account tiller-service-account
```
*deploys helm-charts found at `./helmcharts` to namespace *example_namespace**
Depending on existing deployment with the same name either an installation or an upgrade is performed.
Upgrades always recreate the pods. If the image is pulled depends on `imagePullPolicy` of the container specs.
# Advanced Configuration
## docker build
* `--build-arg`: used to provide build-arguments do `docker build`-command. This is mainly used for `HTTP_PROXY`/`http_proxy`: When you specify `--build-arg HTTP_PROXY=...` the tool adds the build argument `--build-arg http_proxy=...` as well, so lower-case proxy-variables are provided automatically. Nevertheless can you use this option to provide your own build-arguments within the `Dockerfile`
* `--output`: sets the filepath to the file where the built image is saved
## docker publish
* `--import`: sets the filepath to the file from where the image is loaded
* `--docker-config`: sets the content of the file `~/.docker/config.json` which is used by docker to authenticate to the registry. This can contain multiple registry-servers and there credentials. Which registry is used depends on the image name.
* `--name`: sets the name of the image. If you do not wish to publish to [Docker-Hub], you have to specify a server, e.g. `registry.example.com/my-image`. **Be aware that you need to provide credentials in your docker-config if the registry requires authentication.
* `--tags`: sets the tags of the image. Provide multiple `--tag`-options if you wish to tag an image with multiple tags.
## docker deploy
* `--cluster-url`: sets the url to the kube-apiserver. This URL is provided by the k8s-admin.
* `--certificate-authority`: sets the certificate-authority certificate as base64-encoded string. This string is provided by the k8s-admin
* `--token`: sets the bearer token of the service-account as bas64-encoded string. This string is provided by the k8s-admin.
* `--namespace`: sets the k8s-namespace where the deployment is located. This string is provided by the k8s-admin.
* `--service-account`: this is the name of the service-account, that is used to perform the deployment. This string is provided by the k8s-admin
* `--charts`: sets the path where the helm-charts reside.
* `--set`: overrides the values from `Values.yaml` in the helm-charts. Provide multiple `--set`-options if you want to provide multiple overrides.
* `--set-string`: overrides the values from `Values.yaml` in the helm-charts as string. Provide multiple `--set-string`-options if you want to provide multiple overrides.
#!/bin/bash
# saner programming env: these switches turn some bugs into errors
set -o pipefail -o noclobber -o nounset
## declare used variables
cmd=""
image_name="image"
cluster_name="cluster"
context="context"
account="account"
ca_file="${HOME}/k8s-ca.crt"
image_file=""
dockerconfig=""
tags=""
buildargs=""
charts=""
sets=""
setstrings=""
name=""
certificate_authority=""
token=""
namespace=""
cluster_url=""
service_account=""
! getopt --test > /dev/null
if [[ ${PIPESTATUS[0]} -ne 4 ]]; then
echo "I’m sorry, `getopt --test` failed in this environment."
exit 1
fi
OPTIONS=d:,t:,b:,c:,n:,s:
LONGOPTS=docker-config:,tag:,build-arg:,set:,set-string:,charts:,name:,token:,certificate-authority:,namespace:,cluster-url:,service-account:,output:,input:
# -use ! and PIPESTATUS to get exit code with errexit set
# -temporarily store output to be able to check for errors
# -activate quoting/enhanced mode (e.g. by writing out “--options”)
# -pass arguments only via -- "$@" to separate them correctly
! PARSED=$(getopt --options=$OPTIONS --longoptions=$LONGOPTS --name "$0" -- "$@")
if [[ ${PIPESTATUS[0]} -ne 0 ]]; then
# e.g. return value is 1
# then getopt has complained about wrong arguments to stdout
exit 2
fi
# read getopt’s output this way to handle the quoting right:
eval set -- "$PARSED"
# now enjoy the options in order and nicely split until we see --
while true; do
case "$1" in
-d|--docker-config)
dockerconfig="$2"
shift 2
;;
-t|--tag)
tags="${tags} $2"
shift 2
;;
-b|--build-arg)
buildargs="${buildargs} $2"
shift 2
;;
-s|--set)
sets="${sets} $2"
shift 2
;;
--set-string)
setstrings="${setstrings} $2"
shift 2
;;
-c|--charts)
charts="$2"
shift 2
;;
-n|--name)
name="$2"
shift 2
;;
--certificate-authority)
certificate_authority="$2"
shift 2
;;
--token)
token="$2"
shift 2
;;
--namespace)
namespace="$2"
shift 2
;;
--cluster-url)
cluster_url="$2"
shift 2
;;
--service-account)
service_account="$2"
shift 2
;;
--output)
image_file="$2"
shift 2
;;
--input)
image_file="$2"
shift 2
;;
--)
shift
break
;;
*)
echo "Programming error"
echo $1
exit 3
;;
esac
done
prepare_kubectl() {
local out
echo -ne "setting kubectl "
if [ "${cluster_url}" == "" ];then
echo "failed! No cluster url set."
echo "Please be sure to provide a cluster url via --cluster-url"
return 1
fi
if [ "${certificate_authority}" == "" ];then
echo "failed! No certificate authority set."
echo "Please be sure to provide a certificate authority via --certificate-authority"
fi
if [ "${token}" == "" ];then
echo "failed! No token set."
echo "Please be sure to provide a token via --token"
fi
if [ "${namespace}" == "" ];then
echo "failed! No namespace set."
echo "Please be sure to provide a namespace via --namespace"
fi
if [ "${service_account}" == "" ];then
echo "failed! No service-account set."
echo "Please be sure to provide a service-account via --service-account"
fi
out=`rm -rf ~/.kube`
if [ "$?" != "0" ];then
echo "failed"
echo "$out"
return 1
fi;
echo ""
echo -ne "\tSetting cluster..."
echo "${certificate_authority}" | base64 -d >"${ca_file}"
out=`kubectl config set-cluster "${cluster_name}" --certificate-authority=${ca_file} --server="${cluster_url}" --embed-certs=true`
if [ "$?" != "0" ];then
echo "failed"
echo "$out"
return 1
fi;
echo "done"
echo -ne "\tSetting credentials..."
out=`kubectl config set-credentials "${account}" --token="$(echo ${token} | base64 -d)"`
if [ "$?" != "0" ];then
echo "failed"
echo "$out"
return 1
fi;
echo "done"
echo -ne "\tSetting context..."
out=`kubectl config set-context "${context}" --user="${account}" --cluster="${cluster_name}" --namespace="${namespace}"`
if [ "$?" != "0" ];then
echo "failed"
echo "$out"
return 1
fi;
echo "done"
echo -ne "\tActivating context..."
out=`kubectl config use-context "${context}"`
if [ "$?" != "0" ];then
echo "failed"
echo "$out"
return 1
fi;
echo "done"
echo -ne "\tTesting context..."
for resource in deployment service configmap;do
out=`kubectl auth can-i create $resource`
if [ "$?" != "0" ];then
echo "failed"
echo "$out"
cat "${HOME}/.kube/config"
cat "${ca_file}"
return 1
fi
done
echo "done"
return 0
}
prepare_helm() {
echo -ne "Preparing helm..."
out=`helm init --tiller-namespace="${namespace}" --service-account ${service_account} --force-upgrade`
if [ "$?" != "0" ];then
echo "failed!"
echo "$out"
return 1
fi
echo "done"
return 0
}
prepare_helm_command() {
echo -ne "Preparing helm command..."
cmd="helm --tiller-namespace=${namespace} --namespace=${namespace}"
if [ "${name}" == "" ];then
echo "failed"
echo "no deploy name specified"
return 1
fi
if [ "${charts}" == "" ];then
echo "failed"
echo "no chart folder specified"
return 1
fi
out=`helm list -a --tiller-namespace ${namespace} --namespace ${namespace}`
if [ "$?" != "0" ];then
echo "failed"
echo "$out"
return 1
fi
if [ "$(echo $out | grep ${name} | grep DEPLOYED)" == "" ];then
cmd="$cmd install -n ${name}"
else
cmd="$cmd upgrade ${name} --recreate-pods"
fi
for set in ${sets};do
cmd="$cmd --set ${set}"
done
for setstring in ${setstrings}; do
cmd="$cmd --set-string ${set}"
done
cmd="$cmd ${charts}"
echo "done"
return 0
}
helm_deploy() {
echo -ne "Deploying ${name}..."
local out=""
test "$(helm ls --tiller-namespace=${namespace} --namespace=${namespace} --deleted --failed --short | grep ${name})" != "" && helm delete --purge ${name}
out=`$cmd 2>&1`
if [ "$?" != "0" ];then
echo "failed"
echo "$out"
return 1
fi;
echo "done"
}
test_helm_deploy() {
echo -ne "Waiting for successful deploy..."
counter=0
while [ "$(helm ls --tiller-namespace=${namespace} --namespace=${namespace} | grep ${name} | awk -F"\t" '{ print $4; }')" != "DEPLOYED" ]; do
sleep 1
if [ $counter -eq 60 ];then
break
fi
counter=$[$counter+1]
done
if [ $counter -eq 60 ];then
echo "failed"
return 1
fi
echo "done"
return 0
}
prepare_image_publisher() {
echo -ne "setting docker auth config ..."
if [ "${dockerconfig}" == "" ];then
echo "failed! No auth config found"
echo "Please be sure to have provide the config via option docker-config for your build environment"
return 1
fi
rm -rf ~/.docker && mkdir -p ~/.docker && echo "${dockerconfig}" >~/.docker/config.json
if [ "$?" != "0" ];then
echo "failed"
return 1
fi;
echo "done"
return 0
}
prepare_image_builder() {
cmd="docker build --pull"
for arg in $buildargs;do
echo "adding $arg to build command"
cmd="$cmd --build-arg $arg --build-arg ${arg,,}"
done
cmd="$cmd -t ${image_name} ."
return 0
}
save_image() {
local out
echo -ne "saving image ..."
if [ "${image_file}" == "" ];then
echo "failed"
echo "no image name specified"
return 1
fi
out=`docker save --output=${image_file} ${image_name} 2>&1`
if [ "$?" != "0" ];then
echo "failed"
echo "$out"
return 1
fi;
echo "done"
return 0
}
build_image() {
local out
echo -ne "building image..."
out=`$cmd 2>&1`
if [ "$?" != "0" ];then
echo "failed"
echo "$out"
return 1
fi;
echo "done"
return 0
}
import_image() {
local out
echo -ne "importing image..."
if [ ! -e ${image_file} ];then
echo "failed"
echo "${image_file} not found"
return 1
fi
out=`docker load --input=${image_file} 2>&1`
if [ "$?" != "0" ];then
echo "failed"
echo "$out"
return 1
fi
echo "done"
return 0
}
publish_image() {
local out
echo -ne "publishing images "
if [ "${name}" == "" ];then
echo "failed"
echo "no image name given"
return 1
fi
if [ "$tags" == "" ];then
echo "failed"
echo "at least one tag has to be specified"
return 1
fi
echo ""
for tag in $tags; do
echo -ne "\t${name}:${tag}..."
out=`docker tag ${image_name} ${name}:${tag} 2>&1`
if [ "$?" != "0" ];then
echo "failed"
echo "$out"
return 1
fi
out=`docker push ${name}:${tag} 2>&1`
if [ "$?" != "0" ];then
echo "failed"
echo "$out"
return 1
fi
echo "done"
done
return 0
}
# handle non-option arguments
if [[ $# -ne 1 ]]; then
echo "$0: either do build, publish or deploy."
exit 4
fi
case $1 in
build)
prepare_image_builder && build_image && save_image
;;
publish)
prepare_image_publisher && import_image && publish_image
;;
deploy)
prepare_kubectl && prepare_helm && prepare_helm_command && helm_deploy && test_helm_deploy
;;
help)
echo "help"
;;
esac
\ No newline at end of file
version: '2'
services:
deployer:
build: .
volumes:
- ./:/app
- ./assets/deployer:/usr/local/bin/deployer
depends_on:
- docker
environment:
DOCKER_HOST: tcp://docker:2375
docker:
image: docker:dind
privileged: true
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment