diff --git a/README.md b/README.md index 981ade9..70438e6 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,99 @@ -# rundeck +# Rundeck Docker Compose files -Docker Rundeck deployment instructions \ No newline at end of file +Docker Compose files to spin up an instance of Rundeck. + +# How to run + +Add a `COMPOSE_ENV` file and save its location as a shell variable along with the location where this repo lives, here for example `/opt/containers/rundeck` plus all other variables. At [env/fqdn_context.env.example](env/fqdn_context.env.example) you'll find an example environment file. + +When everything's ready start Rundeck with Docker Compose, otherwise head down to [Initial setup](#initial-setup) first. + +## Environment +``` +export COMPOSE_DIR='/opt/containers/rundeck' +export COMPOSE_CTX='ux_vilnius' +export COMPOSE_PROJECT='rundeck-'"${COMPOSE_CTX}" +export COMPOSE_FILE="${COMPOSE_DIR}"'/docker-compose.yml' +export COMPOSE_ENV= +``` + +## Start + +``` +docker compose --project-name "${COMPOSE_PROJECT}" --file "${COMPOSE_FILE}" --env-file "${COMPOSE_ENV}" --profile 'full' up --detach +``` + +# Initial setup + +We're assuming you run Docker Compose workloads with ZFS-based bind mounts. ZFS management, creating a zpool and setting adequate properties for its datasets is out of scope of this document. + +## Datasets + +Create ZFS datasets and set permissions as needed. + +* Parent dateset + ``` + zfs create -o mountpoint=/opt/docker-data 'zpool/docker-data' + ``` + +* Container-specific datasets + ``` + zfs create -p 'zpool/docker-data/rundeck-'"${COMPOSE_CTX}"'/rundeck' + zfs create -p 'zpool/docker-data/rundeck-'"${COMPOSE_CTX}"'/postgres' + ``` + +* Create subdirs + ``` + mkdir -p '/opt/docker-data/rundeck-'"${COMPOSE_CTX}"'/rundeck/'{'.ssh','config','data','projects'} + ``` + +* Prefill content + + * Rundeck settings in `realm.properties` + + At the very least override Rundeck's default `realm.properties` file with one of your own and set a username and a password for local login. Default credentials will otherwise be `admin:admin`. Per [Rundeck's manual on Jetty and JAAS authentication section "PropertyFileLoginModule"](https://docs.rundeck.com/docs/administration/security/authentication.html#propertyfileloginmodule) you're going to need Rundeck's `rundeck.war` file to create a bcrypt hash for your password. Run the official Rundeck Docker image in a throwaway container like so where `rundeck/rundeck:4.13.0` is an example version you want to use: + ``` + docker run \ + --rm \ + --tty \ + --interactive \ + --entrypoint bash \ + rundeck/rundeck:4.13.0 \ + -c 'java -jar /home/rundeck/rundeck.war --encryptpwd Jetty' + ``` + This will download `rundeck/rundeck:4.13.0` if needed and open up something along the lines of: + ``` + Required values are marked with: * + Username (Optional, but necessary for Crypt encoding): + ``` + Type your desired username, type `` and then your plain text password followed by `` again. The whole exchange may look like this: + ``` + Required values are marked with: * + Username (Optional, but necessary for Crypt encoding): + my-username + *Value To Encrypt (The text you want to encrypt): + t0psecr3t + + ==ENCRYPTED OUTPUT== + bcrypt: BCRYPT:$2a$10$jMWQvKbjpmBrKdA0Qi0/n.UvHot1F7Cvf7/Avlv9afknHpbvT6j7y + obfuscate: OBF:1z0f18qk1xtp1vgv1t331vfz1xtt18qq1z0f + md5: MD5:962aefc8c283c13e13d9c990dafdfba9 + crypt: CRYPT:myS5y0c4wMQts + ``` + Put a single line into an otherwise empty `/opt/docker-data/rundeck-'"${COMPOSE_CTX}"'/rundeck/config/realm.properties`: + ``` + my-username: BCRYPT:$2a$10$jMWQvKbjpmBrKdA0Qi0/n.UvHot1F7Cvf7/Avlv9afknHpbvT6j7y,user,admin + ``` + The account `my-username` will have roles `user` and `admin` and it'll be the only existing account when Rundeck starts. + + * SSH `known_hosts` file + + Place an empty `known_hosts` file at `/opt/docker-data/rundeck-'"${COMPOSE_CTX}"'/rundeck/.ssh/known_hosts`. Feel free to optionally prefill it with SSH public host keys. + +* Change ownership + ``` + chown -R 999 '/opt/docker-data/rundeck-'"${COMPOSE_CTX}"'/postgres' + chown -R 1000 '/opt/docker-data/rundeck-'"${COMPOSE_CTX}"'/rundeck' + ``` + +When done head back up to [How to run](#how-to-run). diff --git a/build-context/postgres/Dockerfile b/build-context/postgres/Dockerfile new file mode 100644 index 0000000..3a87dfa --- /dev/null +++ b/build-context/postgres/Dockerfile @@ -0,0 +1,14 @@ +# For the remainder of this Dockerfile EXAMPLE_ARG_FOR_DOCKERFILE will be +# available with a value of 'must_be_available_in_dockerfile', check out the env +# file at 'env/fully.qualified.domain.name.example' for reference. +# ARG EXAMPLE_ARG_FOR_DOCKERFILE + +# Another env var, this one's needed in the example build step below: +# ARG POSTGRES_VERSION + +# Example +# FROM "postgres:${POSTGRES_VERSION}" +# RUN apt-get update && \ +# apt-get -y install \ +# somepackage-6.q16-6-extra && \ +# rm -rf /var/lib/apt/lists/* diff --git a/build-context/postgres/docker-data/.gitkeep b/build-context/postgres/docker-data/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/build-context/postgres/extras/.gitkeep b/build-context/postgres/extras/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/build-context/rundeck/Dockerfile b/build-context/rundeck/Dockerfile new file mode 100644 index 0000000..2c6135e --- /dev/null +++ b/build-context/rundeck/Dockerfile @@ -0,0 +1,14 @@ +# For the remainder of this Dockerfile EXAMPLE_ARG_FOR_DOCKERFILE will be +# available with a value of 'must_be_available_in_dockerfile', check out the env +# file at 'env/fully.qualified.domain.name.example' for reference. +# ARG EXAMPLE_ARG_FOR_DOCKERFILE + +# Another env var, this one's needed in the example build step below: +# ARG RUNDECK_VERSION + +# Example +# FROM "rundeck:${RUNDECK_VERSION}" +# RUN apt-get update && \ +# apt-get -y install \ +# somepackage-6.q16-6-extra && \ +# rm -rf /var/lib/apt/lists/* diff --git a/build-context/rundeck/docker-data/.gitkeep b/build-context/rundeck/docker-data/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/build-context/rundeck/docker-data/.ssh/known_hosts b/build-context/rundeck/docker-data/.ssh/known_hosts new file mode 100644 index 0000000..109e5e5 --- /dev/null +++ b/build-context/rundeck/docker-data/.ssh/known_hosts @@ -0,0 +1,2 @@ +hostname pubkey +another-hostname also-a-pubkey diff --git a/build-context/rundeck/docker-data/config/realm.properties b/build-context/rundeck/docker-data/config/realm.properties new file mode 100644 index 0000000..f73518a --- /dev/null +++ b/build-context/rundeck/docker-data/config/realm.properties @@ -0,0 +1 @@ +my-first-account: BCRYPT:$2a$10$ZcvSOoSNHbnP/foEvJ/6WeKIauGLCr9XCo5.UboJVUJDbHPWrV30K,user,admin diff --git a/build-context/rundeck/extras/.gitkeep b/build-context/rundeck/extras/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/common-settings.yml b/common-settings.yml new file mode 100644 index 0000000..9fd26d7 --- /dev/null +++ b/common-settings.yml @@ -0,0 +1,11 @@ +services: + common-settings: + environment: + TZ: "${TIMEZONE:-Etc/UTC}" + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "10" + compress: "true" + restart: "${RESTARTPOLICY:-unless-stopped}" diff --git a/docker-compose.override.yml b/docker-compose.override.yml new file mode 100644 index 0000000..e52a03f --- /dev/null +++ b/docker-compose.override.yml @@ -0,0 +1,19 @@ +services: + rundeck-build: + image: "rundeck:${RUNDECK_VERSION}" + profiles: ["build", "build-rundeck"] + build: + context: "build-context/rundeck" + dockerfile: Dockerfile + args: + EXAMPLE_ARG_FOR_DOCKERFILE: "${EXAMPLE_ARG_FROM_ENV_FILE}" + RUNDECK_VERSION: "${RUNDECK_VERSION}" + postgres-build: + image: "postgres:${POSTGRES_VERSION}" + profiles: ["build", "build-postgres"] + build: + context: "build-context/postgres" + dockerfile: Dockerfile + args: + EXAMPLE_ARG_FOR_DOCKERFILE: "${EXAMPLE_ARG_FROM_ENV_FILE}" + POSTGRES_VERSION: "${POSTGRES_VERSION}" diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..21f47b6 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,54 @@ +services: + rundeck: + image: "rundeck/rundeck:${RUNDECK_VERSION}" + container_name: "rundeck-rundeck-${CONTEXT}" + networks: + rundeck-default: + profiles: ["full", "rundeck"] + extends: + file: common-settings.yml + service: common-settings + tty: true + ports: + - ${RUNDECK_PORT}:4440 + volumes: + - /opt/docker-data/rundeck-${CONTEXT}/rundeck/config/realm.properties:/home/rundeck/server/config/realm.properties + - /opt/docker-data/rundeck-${CONTEXT}/rundeck/data:/home/rundeck/server/data + - /opt/docker-data/rundeck-${CONTEXT}/rundeck/projects:/home/rundeck/projects + - /opt/docker-data/rundeck-${CONTEXT}/rundeck/.ssh/known_hosts:/home/rundeck/.ssh/known_hosts + environment: + RUNDECK_DATABASE_DRIVER: org.postgresql.Driver + RUNDECK_DATABASE_USERNAME: rundeck + RUNDECK_DATABASE_PASSWORD: rundeck + RUNDECK_DATABASE_URL: jdbc:postgresql://postgres/rundeck?autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true + RUNDECK_GRAILS_URL: https://rundeck.ops.loft.seneve.de + RUNDECK_SERVER_FORWARDED: 'true' + TZ: ${TIMEZONE} + postgres: + image: "postgres:${POSTGRES_VERSION}" + container_name: "rundeck-postgres-${CONTEXT}" + networks: + rundeck-default: + profiles: ["full", "postgres"] + extends: + file: common-settings.yml + service: common-settings + volumes: + - /opt/docker-data/rundeck-${CONTEXT}/postgres:/var/lib/postgresql/data + ports: + - ${POSTGRES_PORT}:5432 + environment: + POSTGRES_DB: rundeck + POSTGRES_USER: rundeck + POSTGRES_PASSWORD: rundeck + TZ: ${TIMEZONE} +networks: + rundeck-default: + name: rundeck-${CONTEXT} + driver: bridge + driver_opts: + com.docker.network.enable_ipv6: "false" + ipam: + driver: default + config: + - subnet: ${SUBNET} diff --git a/env/fqdn_context.env.example b/env/fqdn_context.env.example new file mode 100644 index 0000000..f98dccc --- /dev/null +++ b/env/fqdn_context.env.example @@ -0,0 +1,32 @@ +CONTEXT=ux_vilnius + + + +# Set something sensible here and uncomment +# --- +# RUNDECK_VERSION=x.y.z +# POSTGRES_VERSION=x.y.z + + + +# Feel free to leave defaults. They apply while these vars are commented out +# --- +# RESTARTPOLICY=unless-stopped +# TIMEZONE=Etc/UTC + + + +# Subnet to use for this Docker Compose project. Docker defaults to +# container networks in prefix 172.16.0.0/12 which is 1 million addresses in +# the range from 172.16.0.0 to 172.31.255.255. Docker uses 172.17.0.0/16 for +# itself. Use any sensible prefix in 172.16.0.0/12 here except for Docker's +# own 172.17.0.0/16. +# --- +SUBNET=172.30.95.0/24 + + + +# See 'docker-compose.override.yml' for how to make a variable available in +# a Dockerfile +# --- +# EXAMPLE_ARG_FROM_ENV_FILE=must_be_available_in_dockerfile