diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..ba10274 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,4 @@ +# Changelog + +## 0.1.0 + - Initial release diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..e6b8112 --- /dev/null +++ b/Makefile @@ -0,0 +1,20 @@ +NAME = osixia/keepalived +VERSION = 0.1.0 + +.PHONY: all build test tag_latest release + +all: build + +build: + docker build -t $(NAME):$(VERSION) --rm image + +test: + env NAME=$(NAME) VERSION=$(VERSION) bats test/test.bats + +tag_latest: + docker tag -f $(NAME):$(VERSION) $(NAME):latest + +release: build test tag_latest + @if ! docker images $(NAME) | awk '{ print $$2 }' | grep -q -F $(VERSION); then echo "$(NAME) version $(VERSION) is not yet built. Please run 'make build'"; false; fi + docker push $(NAME) + @echo "*** Don't forget to run 'twgit release/hotfix finish' :)" diff --git a/README.md b/README.md index 5f1c132..08206ae 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,10 @@ -# docker-keepalived -A docker image to run Keepalived +# osixia/keepalived + +A docker image to run Keepalived. +> [keepalived.org](http://keepalived.org/) + +## Quick start + +This image need to be run with : --privileged --net=host + + docker run --privileged --net=host -d osixia/keepalived diff --git a/image/Dockerfile b/image/Dockerfile new file mode 100644 index 0000000..8d6015f --- /dev/null +++ b/image/Dockerfile @@ -0,0 +1,35 @@ +FROM osixia/baseimage:0.10.4 +MAINTAINER Bertrand Gouny + +# Keepalived version +ENV KEEPALIVED_VERSION 1.2.17 + +# Use baseimage-docker's init system. +CMD ["/sbin/my_init"] + +# Install Keepalived +RUN apt-get -y update \ + && LC_ALL=C DEBIAN_FRONTEND=noninteractive apt-get install -y \ + make gcc libssl-dev \ + && curl -o keepalived.tar.gz -SL http://keepalived.org/software/keepalived-${KEEPALIVED_VERSION}.tar.gz \ + && mkdir -p /osixia/keepalived-sources \ + && tar -xzf keepalived.tar.gz --strip 1 -C /osixia/keepalived-sources \ + && cd osixia/keepalived-sources \ + && ./configure --with-kernel-dir=/lib/modules/$(uname -r)/build \ + && make && make install \ + && cd - && mkdir -p /etc/keepalived \ + && apt-get remove -y --purge --auto-remove make gcc libssl-dev + +# Add Keepalived assets +ADD service/keepalived/assets /osixia/keepalived + +# Clean all +RUN rm keepalived.tar.gz \ + && apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* + +# Add default env variables +ADD env.yml /etc/env.yml + +# Add Keepalived container start config & daemon +ADD service/keepalived/container-start.sh /etc/my_init.d/keepalived +ADD service/keepalived/daemon.sh /etc/service/keepalived/run diff --git a/image/env.yml b/image/env.yml new file mode 100644 index 0000000..3121883 --- /dev/null +++ b/image/env.yml @@ -0,0 +1,12 @@ +KEEPALIVED_INTERFACE: p4p1 +KEEPALIVED_PASSWORD: d0cker + +# for electing MASTER, highest priority wins. +# to be MASTER, make 50 more than other machines +KEEPALIVED_PRIORITY: 150 + +KEEPALIVED_UNICAST_PEERS: + - 192.168.1.10 + +KEEPALIVED_VIRTUAL_IPS: + - 192.168.1.231 diff --git a/image/service/keepalived/assets/README.md b/image/service/keepalived/assets/README.md new file mode 100644 index 0000000..a4695d4 --- /dev/null +++ b/image/service/keepalived/assets/README.md @@ -0,0 +1 @@ +Add your custom keepalived.conf file here or mount one at docker run to /etc/keepalived/keepalived.conf diff --git a/image/service/keepalived/assets/keepalived.conf b/image/service/keepalived/assets/keepalived.conf new file mode 100644 index 0000000..e92f9d7 --- /dev/null +++ b/image/service/keepalived/assets/keepalived.conf @@ -0,0 +1,25 @@ +vrrp_instance vip-1 { + interface {{ keepalived_interface }} + + track_interface { + {{ keepalived_interface }} + } + + state MASTER + virtual_router_id 51 + priority {{ keepalived_priority }} + nopreempt + + unicast_peer { + {{ keepalived_unicast_peers }} + } + + virtual_ipaddress { + {{ keepalived_virtual_ips }} + } + + authentication { + auth_type PASS + auth_pass {{ keepalived_password }} + } +} diff --git a/image/service/keepalived/container-start.sh b/image/service/keepalived/container-start.sh new file mode 100755 index 0000000..9b58ce9 --- /dev/null +++ b/image/service/keepalived/container-start.sh @@ -0,0 +1,61 @@ +#!/bin/bash -e + +FIRST_START_DONE="/etc/docker-keepalived-first-start-done" + +# container first start +if [ ! -e "$FIRST_START_DONE" ]; then + + # config folder is empty use bootstrap config if available + if [ ! -e /etc/keepalived/keepalived.conf ]; then + echo "No keepalived.conf provided using image default one" + if [ ! -e /osixia/keepalived/keepalived.conf ]; then + echo "Error: No default keepalived.conf found in /osixia/keepalived/keepalived.conf" + exit 1 + else + + ln -s /osixia/keepalived/keepalived.conf /etc/keepalived/keepalived.conf + + # + # bootstrap config + # + sed -i "s|{{ keepalived_interface }}|$KEEPALIVED_INTERFACE|g" /etc/keepalived/keepalived.conf + sed -i "s|{{ keepalived_priority }}|$KEEPALIVED_PRIORITY|g" /etc/keepalived/keepalived.conf + sed -i "s|{{ keepalived_password }}|$KEEPALIVED_PASSWORD|g" /etc/keepalived/keepalived.conf + + # unicast peers + KEEPALIVED_UNICAST_PEERS=($KEEPALIVED_UNICAST_PEERS) + for peer in "${KEEPALIVED_UNICAST_PEERS[@]}" + do + # it's just a peer + # stored in a variable + if [ -n "${!peer}" ]; then + sed -i "s|{{ keepalived_unicast_peers }}|${!peer}\n {{ keepalived_unicast_peers }}|g" /etc/keepalived/keepalived.conf + # directly + else + sed -i "s|{{ keepalived_unicast_peers }}|${peer}\n {{ keepalived_unicast_peers }}|g" /etc/keepalived/keepalived.conf + fi + done + sed -i "/{{ keepalived_unicast_peers }}/d" /etc/keepalived/keepalived.conf + + # virtual ips + KEEPALIVED_VIRTUAL_IPS=($KEEPALIVED_VIRTUAL_IPS) + for vip in "${KEEPALIVED_VIRTUAL_IPS[@]}" + do + # it's just a peer + # stored in a variable + if [ -n "${!vip}" ]; then + sed -i "s|{{ keepalived_virtual_ips }}|${!vip}\n {{ keepalived_virtual_ips }}|g" /etc/keepalived/keepalived.conf + # directly + else + sed -i "s|{{ keepalived_virtual_ips }}|${vip}\n {{ keepalived_virtual_ips }}|g" /etc/keepalived/keepalived.conf + fi + done + sed -i "/{{ keepalived_virtual_ips }}/d" /etc/keepalived/keepalived.conf + fi + + fi + + touch $FIRST_START_DONE +fi + +exit 0 diff --git a/image/service/keepalived/daemon.sh b/image/service/keepalived/daemon.sh new file mode 100755 index 0000000..00750a1 --- /dev/null +++ b/image/service/keepalived/daemon.sh @@ -0,0 +1,2 @@ +#!/bin/bash -e +exec /usr/local/sbin/keepalived -f /etc/keepalived/keepalived.conf --dont-fork --log-console -D -d diff --git a/test/test.bats b/test/test.bats new file mode 100644 index 0000000..fb7fc13 --- /dev/null +++ b/test/test.bats @@ -0,0 +1,9 @@ +#!/usr/bin/env bats +load test_helper + +@test "image build" { + + run build_image + [ "$status" -eq 0 ] + +} diff --git a/test/test_helper.bash b/test/test_helper.bash new file mode 100644 index 0000000..31bdd43 --- /dev/null +++ b/test/test_helper.bash @@ -0,0 +1,101 @@ +setup() { + IMAGE_NAME="$NAME:$VERSION" +} + +# function relative to the current container / image +build_image() { + #disable outputs + docker build -t $IMAGE_NAME $BATS_TEST_DIRNAME/../image &> /dev/null +} + +run_image() { + CONTAINER_ID=$(docker run $@ -d $IMAGE_NAME) + CONTAINER_IP=$(get_container_ip_by_cid $CONTAINER_ID) +} + +start_container() { + start_containers_by_cid $CONTAINER_ID +} + +stop_container() { + stop_containers_by_cid $CONTAINER_ID +} + +remove_container() { + remove_containers_by_cid $CONTAINER_ID +} + +clear_container() { + stop_containers_by_cid $CONTAINER_ID + remove_containers_by_cid $CONTAINER_ID +} + +is_service_running() { + is_service_running_by_cid $CONTAINER_ID $1 +} + +wait_service() { + wait_service_by_cid $CONTAINER_ID $@ +} + + +# generic functions +get_container_ip_by_cid() { + local IP=$(docker inspect -f "{{ .NetworkSettings.IPAddress }}" $1) + echo "$IP" +} + +start_containers_by_cid() { + for cid in "$@" + do + #disable outputs + docker start $cid &> /dev/null + done +} + +stop_containers_by_cid() { + for cid in "$@" + do + #disable outputs + docker stop $cid &> /dev/null + done +} + +remove_containers_by_cid() { + for cid in "$@" + do + #disable outputs + docker rm $cid &> /dev/null + done +} + +clear_containers_by_cid() { + stop_containers_by_cid $@ + remove_containers_by_cid $@ +} + +is_service_running_by_cid() { + docker exec $1 ps cax | grep $2 > /dev/null +} + +wait_service_by_cid() { + + cid=$1 + + # first wait image init end + while ! is_service_running_by_cid $cid syslog-ng + do + sleep 1 + done + + for service in "${@:2}" + do + # wait service + while ! is_service_running_by_cid $cid $service + do + sleep 1 + done + done + + sleep 5 +} \ No newline at end of file