Merge pull request 'add-readme-to-docker-compose' (#6) from add-readme-to-docker-compose into master
Reviewed-on: #6
This commit is contained in:
commit
ccfaaac36d
5
.gitignore
vendored
5
.gitignore
vendored
@ -2,6 +2,9 @@
|
|||||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
||||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||||
|
|
||||||
|
# pyenv
|
||||||
|
.python-version
|
||||||
|
|
||||||
# User-specific stuff
|
# User-specific stuff
|
||||||
.idea/**/workspace.xml
|
.idea/**/workspace.xml
|
||||||
.idea/**/tasks.xml
|
.idea/**/tasks.xml
|
||||||
@ -206,6 +209,8 @@ venv/
|
|||||||
ENV/
|
ENV/
|
||||||
env.bak/
|
env.bak/
|
||||||
venv.bak/
|
venv.bak/
|
||||||
|
!docker-compose/examples/*/env
|
||||||
|
!docker-compose/{{ cookiecutter.__project_slug }}/env
|
||||||
|
|
||||||
# Spyder project settings
|
# Spyder project settings
|
||||||
.spyderproject
|
.spyderproject
|
||||||
|
179
README.md
179
README.md
@ -2,17 +2,17 @@
|
|||||||
|
|
||||||
Project directory structure templates for the Python Cookiecutter package.
|
Project directory structure templates for the Python Cookiecutter package.
|
||||||
|
|
||||||
## What's Cookiecutter?
|
# What's Cookiecutter?
|
||||||
|
|
||||||
The Python package [Cookiecutter](https://github.com/cookiecutter/cookiecutter) assists in creating directory structure for whatever purpose you need such as for example a new Python project, an Ansible role or a `docker-compose` project - anything really that benefits from a unifirm reproducible directory structure. If you've ever wanted to put project structure best practices into version control then Cookiecutter's here to help.
|
The Python package [Cookiecutter](https://github.com/cookiecutter/cookiecutter) assists in creating directory structure for whatever purpose you need such as for example a new Python project, an Ansible role or a `docker-compose` project - anything really that benefits from a unifirm reproducible directory structure. If you've ever wanted to put project structure best practices into version control then Cookiecutter's here to help.
|
||||||
|
|
||||||
Cookiecutter is governed by so-called Cookiecutter templates, most of its magic inside Cookiecutter templates happens via the [Jinja2 template engine](https://palletsprojects.com/p/jinja/). You'll feel right at home if you're familiar with Ansible.
|
Cookiecutter is governed by so-called Cookiecutter templates, most of its magic inside Cookiecutter templates happens via the [Jinja2 template engine](https://palletsprojects.com/p/jinja/). You'll feel right at home if you're familiar with Ansible.
|
||||||
|
|
||||||
## Repo layout
|
# Repo layout
|
||||||
|
|
||||||
Each subdirectory in this repo is a Cookiecutter template, you'll recognize them from their telltale `cookiecutter.json` files. Directories usually also have a readme file explaining more about each individual template.
|
Each subdirectory in this repo is a Cookiecutter template, you'll recognize them from their telltale `cookiecutter.json` files. Directories usually also have a readme file explaining more about each individual template.
|
||||||
|
|
||||||
## Get started
|
# Get started
|
||||||
|
|
||||||
Get Cookiecutter like so:
|
Get Cookiecutter like so:
|
||||||
```
|
```
|
||||||
@ -57,3 +57,176 @@ The end result is a directory structure that has everything you need to hit the
|
|||||||
└── env
|
└── env
|
||||||
└── fully.qualified.domain.name.example
|
└── fully.qualified.domain.name.example
|
||||||
```
|
```
|
||||||
|
|
||||||
|
# Developing
|
||||||
|
|
||||||
|
To change Cookiecutter templates get yourself an environment then make, test and commit your changes. First things first, the environment.
|
||||||
|
|
||||||
|
## Environment
|
||||||
|
|
||||||
|
Get yourself a Python virtual environment. In this example we're assuming you're running a Linux operating system and you'll be using [pyenv](https://github.com/pyenv/pyenv) to manage virtual environments.
|
||||||
|
|
||||||
|
### pyenv
|
||||||
|
|
||||||
|
- Install pyenv with what they are calling their automatic installer. Feel free to also read up on pyenv on their [GitHub project page](https://github.com/pyenv/pyenv).
|
||||||
|
```
|
||||||
|
curl https://pyenv.run | bash
|
||||||
|
```
|
||||||
|
|
||||||
|
- Following the installer's instruction add at least the following commands to your `~/.bashrc` file
|
||||||
|
```
|
||||||
|
export PYENV_ROOT="$HOME/.pyenv"
|
||||||
|
command -v pyenv >/dev/null || export PATH="$PYENV_ROOT/bin:$PATH"
|
||||||
|
eval "$(pyenv init -)"
|
||||||
|
```
|
||||||
|
|
||||||
|
- You will also likely want to add this line which will make sure pyenv auto-activates venvs when you navigate into certain directories. More on that down at [venv](#venv).
|
||||||
|
```
|
||||||
|
eval "$(pyenv virtualenv-init -)"
|
||||||
|
```
|
||||||
|
|
||||||
|
- Also make sure `~/.bashrc` gets loaded for example by including this in a `~/.bash_profile` file
|
||||||
|
```
|
||||||
|
[[ -f ~/.bashrc ]] && . ~/.bashrc
|
||||||
|
```
|
||||||
|
|
||||||
|
- Reload `~/.bashrc`
|
||||||
|
```
|
||||||
|
source ~/.bashrc
|
||||||
|
```
|
||||||
|
|
||||||
|
### Python
|
||||||
|
|
||||||
|
- Update pyenv's package list
|
||||||
|
```
|
||||||
|
pyenv update
|
||||||
|
```
|
||||||
|
- Pick a Python version you like, for example copy-paste `3.11.4`:
|
||||||
|
```
|
||||||
|
pyenv install --list | less
|
||||||
|
|
||||||
|
...
|
||||||
|
3.10.12
|
||||||
|
3.11.0
|
||||||
|
3.11-dev
|
||||||
|
3.11.1
|
||||||
|
3.11.2
|
||||||
|
3.11.3
|
||||||
|
3.11.4
|
||||||
|
3.12.0b2
|
||||||
|
3.12-dev
|
||||||
|
3.13-dev
|
||||||
|
...
|
||||||
|
```
|
||||||
|
- Install Python, wait for compilation to finish on your machine
|
||||||
|
```
|
||||||
|
pyenv install 3.11.4
|
||||||
|
```
|
||||||
|
|
||||||
|
### Repo
|
||||||
|
|
||||||
|
- Clone this repo
|
||||||
|
```
|
||||||
|
git clone https://quico.space/Quico/py-cookiecutter-templates.git ~/py-cookiecutter-templates
|
||||||
|
```
|
||||||
|
|
||||||
|
### venv
|
||||||
|
|
||||||
|
- Create a virtual environment where `3.11.4` is the Python version you want to use in this venv and `cookiecutter-3.11.4` is the name of your venv. Adding or dropping the Python version from your venv name comes down to personal preference.
|
||||||
|
```
|
||||||
|
pyenv virtualenv 3.11.4 cookiecutter-3.11.4
|
||||||
|
```
|
||||||
|
|
||||||
|
- In your repo path `~/py-cookiecutter-templates` create a `.python-version` file to tell pyenv to always activate your desired venv when inside this dir.
|
||||||
|
```
|
||||||
|
cd ~/py-cookiecutter-templates
|
||||||
|
pyenv local cookiecutter-3.11.4
|
||||||
|
```
|
||||||
|
pyenv will immediately prefix your shell's `${PS1}` prompt with the venv name.
|
||||||
|
```
|
||||||
|
(cookiecutter-3.11.4) [✔ 23:19 user@machine py-cookiecutter-templates]$
|
||||||
|
```
|
||||||
|
It will deactivate the venv and drop its prefix as soon as you navigate out of this dir.
|
||||||
|
```
|
||||||
|
(cookiecutter-3.11.4) [✔ 23:19 user@machine py-cookiecutter-templates]$ cd
|
||||||
|
[✔ 23:19 user@machine ~]$
|
||||||
|
```
|
||||||
|
For now though stay in `~/py-cookiecutter-templates`, you're going to want to pip-install [cookiecutter](https://pypi.org/project/cookiecutter).
|
||||||
|
|
||||||
|
- Upgrade `pip`
|
||||||
|
```
|
||||||
|
pip install --upgrade pip
|
||||||
|
```
|
||||||
|
|
||||||
|
- Install [cookiecutter](https://pypi.org/project/cookiecutter)
|
||||||
|
```
|
||||||
|
pip install cookiecutter
|
||||||
|
```
|
||||||
|
|
||||||
|
All done, your environment is set up.
|
||||||
|
|
||||||
|
## Change
|
||||||
|
|
||||||
|
Make some code changes, for example to the Docker Compose Cookiecutter template. When you're happy run your local Cookiecutter template to see how your changes are rendering.
|
||||||
|
|
||||||
|
- Create `/tmp/cookiecutter-docker-compose`
|
||||||
|
```
|
||||||
|
mkdir '/tmp/cookiecutter-docker-compose'
|
||||||
|
```
|
||||||
|
|
||||||
|
- Render a Docker Compose directory into your output directory, answer Cookiecutter's prompts:
|
||||||
|
```
|
||||||
|
# cookiecutter ~/py-cookiecutter-templates \
|
||||||
|
--directory docker-compose \
|
||||||
|
--output-dir /tmp/cookiecutter-docker-compose
|
||||||
|
|
||||||
|
project_slug [dir-name]: mydir
|
||||||
|
service [mydir]: myservice
|
||||||
|
component_list [myservice]: mycomponent_one,mycomponent_two
|
||||||
|
context [ctx]: ux_novosibirsk
|
||||||
|
```
|
||||||
|
|
||||||
|
- Observe that in `/tmp/cookiecutter-docker-compose` you now have your rendered Docker Compose dir:
|
||||||
|
```
|
||||||
|
# tree /tmp/cookiecutter-docker-compose
|
||||||
|
|
||||||
|
/tmp/cookiecutter-docker-compose
|
||||||
|
└── mydir
|
||||||
|
├── build-context
|
||||||
|
│ ├── mycomponent_one
|
||||||
|
│ │ ├── docker-data
|
||||||
|
│ │ ├── Dockerfile
|
||||||
|
│ │ └── extras
|
||||||
|
│ └── mycomponent_two
|
||||||
|
│ ├── docker-data
|
||||||
|
│ ├── Dockerfile
|
||||||
|
│ └── extras
|
||||||
|
├── common-settings.yml
|
||||||
|
├── docker-compose.override.yml
|
||||||
|
├── docker-compose.yml
|
||||||
|
└── env
|
||||||
|
└── fqdn_context.env.example
|
||||||
|
```
|
||||||
|
|
||||||
|
- For rapid testing you will most likely want to not type prompt answers repeatedly. Give them as command line arguments instead, also specify `--no-input` to suppress prompts:
|
||||||
|
```
|
||||||
|
cookiecutter ~/py-cookiecutter-templates \
|
||||||
|
--no-input \
|
||||||
|
--directory docker-compose \
|
||||||
|
--output-dir /tmp/cookiecutter-docker-compose \
|
||||||
|
project_slug=mydir \
|
||||||
|
service=myservice \
|
||||||
|
component_list=mycomponent_one,mycomponent_two \
|
||||||
|
context=ux_novosibirsk
|
||||||
|
```
|
||||||
|
|
||||||
|
## Commit prep
|
||||||
|
|
||||||
|
When you're about ready to commit changes into this repo check the following bullet points.
|
||||||
|
|
||||||
|
- Did you update the [Cookiecutter template README.md file](docker-compose/README.md), here for example for Docker Compose?
|
||||||
|
- Change in behavior?
|
||||||
|
- An updated example directory layout?
|
||||||
|
- Updated example file content?
|
||||||
|
- Did you commit a new completely rendered example directory structure into [docker-compose/examples](docker-compose/examples) dir?
|
||||||
|
- Did you change something that affects existing example directories? If so rerender them.
|
||||||
|
@ -13,6 +13,10 @@ project_slug [dir-name]: grafana
|
|||||||
service [grafana]:
|
service [grafana]:
|
||||||
component_list [grafana]: grafana,nginx
|
component_list [grafana]: grafana,nginx
|
||||||
context [ctx]: cncf
|
context [ctx]: cncf
|
||||||
|
Select build:
|
||||||
|
1 - no
|
||||||
|
2 - yes
|
||||||
|
Choose from 1, 2 [1]:
|
||||||
```
|
```
|
||||||
|
|
||||||
Done, directory structure and files for your next `docker-compose` project are ready for you to hit the ground running.
|
Done, directory structure and files for your next `docker-compose` project are ready for you to hit the ground running.
|
||||||
@ -60,13 +64,13 @@ Your four answers translate as follows into rendered files.
|
|||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
4. The last prompt for a `context` is a generic string to help you distinguish deployments. It can be whatever you want such as for example a team name, here `cncf` which shows up as a preset in an example env file. It defaults to `ctx` just so it can't be empty.
|
4. The `context` prompt is a generic string to help you distinguish deployments. It can be whatever you want such as for example a team name, here `cncf` which shows up as a preset in an example env file. It defaults to `ctx` just so it can't be empty.
|
||||||
```
|
```
|
||||||
.
|
.
|
||||||
└── grafana
|
└── grafana
|
||||||
...
|
...
|
||||||
└── env
|
└── env
|
||||||
└── fully.qualified.domain.name.example <---
|
└── fqdn_context.env.example <---
|
||||||
```
|
```
|
||||||
|
|
||||||
Which then looks like:
|
Which then looks like:
|
||||||
@ -75,6 +79,12 @@ Your four answers translate as follows into rendered files.
|
|||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
5. Prompt `build` is a yes-no question. Cookiecutter will create a `README.md` file with copy-pastable Docker Compose commands pre-filled. If you answer `yes` to this prompt `README.md` will contain an example paragraph that explains the build process along the lines of:
|
||||||
|
```
|
||||||
|
docker compose ... --profile 'build' build
|
||||||
|
```
|
||||||
|
Whereas by answering `no` (or just hitting `<Enter>` to accept the default of `no`) no such paragraph will be added to the example Markdown file. Build instructions are only really needed if you need to locally build a derivative image.
|
||||||
|
|
||||||
Also check out [the Caveats section](#caveats) at the end to learn what this template does not do well.
|
Also check out [the Caveats section](#caveats) at the end to learn what this template does not do well.
|
||||||
|
|
||||||
## Result
|
## Result
|
||||||
@ -132,7 +142,7 @@ Consider Cookiecutter's project directory and rendered files a starting point. I
|
|||||||
|
|
||||||
Imagine if you will a service that consists of [Infinispan](https://infinispan.org/) among other things. In Docker Hub's content-addressable image store Infinispan's location is at `infinispan/server` so you obviously want that exact string with a forward slash to show up in your `docker-compose.yml` as the `image:` key's value, same with your `Dockerfile`. The `image:` key's value comes from what you enter in Cookiecutter's `component_list` prompt. Component strings are then used to also pre-fill the `volumes:` key.
|
Imagine if you will a service that consists of [Infinispan](https://infinispan.org/) among other things. In Docker Hub's content-addressable image store Infinispan's location is at `infinispan/server` so you obviously want that exact string with a forward slash to show up in your `docker-compose.yml` as the `image:` key's value, same with your `Dockerfile`. The `image:` key's value comes from what you enter in Cookiecutter's `component_list` prompt. Component strings are then used to also pre-fill the `volumes:` key.
|
||||||
|
|
||||||
This will cause obvious issues (but the `image:` key is kinda correct):
|
_**This**_ will cause obvious issues (but the `image:` key is kinda correct):
|
||||||
```
|
```
|
||||||
services:
|
services:
|
||||||
infinispan/server:
|
infinispan/server:
|
||||||
@ -140,7 +150,7 @@ services:
|
|||||||
container_name: "cacheman-infinispan/server-${CONTEXT}"
|
container_name: "cacheman-infinispan/server-${CONTEXT}"
|
||||||
```
|
```
|
||||||
|
|
||||||
This won't cause issues (but you'll have to then go in and manually change the `image:` key to use `infinispan/server`):
|
_**This**_ won't cause issues (but you'll have to then go in and manually change the `image:` key to use `infinispan/server`):
|
||||||
```
|
```
|
||||||
services:
|
services:
|
||||||
infinispan:
|
infinispan:
|
||||||
|
@ -6,5 +6,6 @@
|
|||||||
"component_list": "{{ cookiecutter.__service_slug }}",
|
"component_list": "{{ cookiecutter.__service_slug }}",
|
||||||
"__component_list_slug": "{{ cookiecutter.component_list.lower().replace(' ', '_').replace('-', '_') }}",
|
"__component_list_slug": "{{ cookiecutter.component_list.lower().replace(' ', '_').replace('-', '_') }}",
|
||||||
"context": "ctx",
|
"context": "ctx",
|
||||||
"__context_slug": "{{ cookiecutter.context.lower().replace(' ', '_').replace('-', '_') }}"
|
"__context_slug": "{{ cookiecutter.context.lower().replace(' ', '_').replace('-', '_') }}",
|
||||||
|
"build": ["no", "yes"]
|
||||||
}
|
}
|
||||||
|
@ -5,15 +5,18 @@ services:
|
|||||||
networks:
|
networks:
|
||||||
grafana-default:
|
grafana-default:
|
||||||
profiles: ["full", "grafana"]
|
profiles: ["full", "grafana"]
|
||||||
|
depends_on:
|
||||||
|
nginx:
|
||||||
|
condition: service_healthy
|
||||||
extends:
|
extends:
|
||||||
file: common-settings.yml
|
file: common-settings.yml
|
||||||
service: common-settings
|
service: common-settings
|
||||||
ports:
|
ports:
|
||||||
# - "8080:80"
|
# - "8080:80"
|
||||||
volumes:
|
volumes:
|
||||||
# - /opt/docker-data/grafana-grafana-${CONTEXT}/grafana/data/db:/usr/lib/grafana
|
# - /opt/docker-data/grafana-${CONTEXT}/grafana/data/db:/usr/lib/grafana
|
||||||
# - /opt/docker-data/grafana-grafana-${CONTEXT}/grafana/data/logs:/var/log/grafana
|
# - /opt/docker-data/grafana-${CONTEXT}/grafana/data/logs:/var/log/grafana
|
||||||
# - /opt/docker-data/grafana-grafana-${CONTEXT}/grafana/config:/etc/grafana
|
# - /opt/docker-data/grafana-${CONTEXT}/grafana/config:/etc/grafana
|
||||||
environment:
|
environment:
|
||||||
# GRAFANA_USER: ${GRAFANA_USER}
|
# GRAFANA_USER: ${GRAFANA_USER}
|
||||||
# GRAFANA_PASSWORD: ${GRAFANA_PASSWORD}
|
# GRAFANA_PASSWORD: ${GRAFANA_PASSWORD}
|
||||||
@ -23,15 +26,21 @@ services:
|
|||||||
networks:
|
networks:
|
||||||
grafana-default:
|
grafana-default:
|
||||||
profiles: ["full", "nginx"]
|
profiles: ["full", "nginx"]
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "fping", "--count=1", "${GRAFANA_VIP}", "--period=500", "--quiet"]
|
||||||
|
interval: 3s
|
||||||
|
timeout: 1s
|
||||||
|
retries: 60
|
||||||
|
start_period: 2s
|
||||||
extends:
|
extends:
|
||||||
file: common-settings.yml
|
file: common-settings.yml
|
||||||
service: common-settings
|
service: common-settings
|
||||||
ports:
|
ports:
|
||||||
# - "8080:80"
|
# - "8080:80"
|
||||||
volumes:
|
volumes:
|
||||||
# - /opt/docker-data/grafana-nginx-${CONTEXT}/nginx/data/db:/usr/lib/nginx
|
# - /opt/docker-data/grafana-${CONTEXT}/nginx/data/db:/usr/lib/nginx
|
||||||
# - /opt/docker-data/grafana-nginx-${CONTEXT}/nginx/data/logs:/var/log/nginx
|
# - /opt/docker-data/grafana-${CONTEXT}/nginx/data/logs:/var/log/nginx
|
||||||
# - /opt/docker-data/grafana-nginx-${CONTEXT}/nginx/config:/etc/nginx
|
# - /opt/docker-data/grafana-${CONTEXT}/nginx/config:/etc/nginx
|
||||||
environment:
|
environment:
|
||||||
# NGINX_USER: ${NGINX_USER}
|
# NGINX_USER: ${NGINX_USER}
|
||||||
# NGINX_PASSWORD: ${NGINX_PASSWORD}
|
# NGINX_PASSWORD: ${NGINX_PASSWORD}
|
||||||
@ -44,4 +53,4 @@ networks:
|
|||||||
ipam:
|
ipam:
|
||||||
driver: default
|
driver: default
|
||||||
config:
|
config:
|
||||||
# - subnet: 172.21.184.0/24
|
- subnet: ${SUBNET}
|
||||||
|
@ -6,6 +6,7 @@ CONTEXT=cncf
|
|||||||
# ---
|
# ---
|
||||||
# GRAFANA_VERSION=x.y.z
|
# GRAFANA_VERSION=x.y.z
|
||||||
# NGINX_VERSION=x.y.z
|
# NGINX_VERSION=x.y.z
|
||||||
|
# GRAFANA_VIP=10.1.1.2
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -24,6 +25,16 @@ CONTEXT=cncf
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# 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
|
# See 'docker-compose.override.yml' for how to make a variable available in
|
||||||
# a Dockerfile
|
# a Dockerfile
|
||||||
# ---
|
# ---
|
@ -25,4 +25,4 @@ networks:
|
|||||||
ipam:
|
ipam:
|
||||||
driver: default
|
driver: default
|
||||||
config:
|
config:
|
||||||
# - subnet: 172.21.184.0/24
|
- subnet: ${SUBNET}
|
||||||
|
@ -5,6 +5,7 @@ CONTEXT=fsf
|
|||||||
# Set something sensible here and uncomment
|
# Set something sensible here and uncomment
|
||||||
# ---
|
# ---
|
||||||
# VAULT_VERSION=x.y.z
|
# VAULT_VERSION=x.y.z
|
||||||
|
# VAULT_VIP=10.1.1.2
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -23,6 +24,16 @@ CONTEXT=fsf
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# 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
|
# See 'docker-compose.override.yml' for how to make a variable available in
|
||||||
# a Dockerfile
|
# a Dockerfile
|
||||||
# ---
|
# ---
|
80
docker-compose/{{ cookiecutter.__project_slug }}/README.md
Normal file
80
docker-compose/{{ cookiecutter.__project_slug }}/README.md
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
# {{ cookiecutter.__service_slug.capitalize() }} Docker Compose files
|
||||||
|
|
||||||
|
Docker Compose files to spin up an instance of {{ cookiecutter.__service_slug.capitalize() }}.
|
||||||
|
|
||||||
|
# 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/{{ cookiecutter.__project_slug }}` 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 {{ cookiecutter.__service_slug.capitalize() }} with Docker Compose, otherwise head down to [Initial setup](#initial-setup) first.
|
||||||
|
|
||||||
|
## Environment
|
||||||
|
```
|
||||||
|
export COMPOSE_DIR='/opt/containers/{{ cookiecutter.__project_slug }}'
|
||||||
|
export COMPOSE_CTX='{{ cookiecutter.__context_slug }}'
|
||||||
|
export COMPOSE_PROJECT='{{ cookiecutter.__service_slug }}-'"${COMPOSE_CTX}"
|
||||||
|
export COMPOSE_FILE="${COMPOSE_DIR}"'/docker-compose.yml'{% if cookiecutter.build == "yes" %}
|
||||||
|
export COMPOSE_OVERRIDE="${COMPOSE_DIR%/}"'/docker-compose.override.yml'{% endif %}
|
||||||
|
export COMPOSE_ENV=<add accordingly>
|
||||||
|
```
|
||||||
|
|
||||||
|
{%- if cookiecutter.build == "yes" %}
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
|
```
|
||||||
|
docker compose --project-name "${COMPOSE_PROJECT}" --file "${COMPOSE_FILE}" --file "${COMPOSE_OVERRIDE}" --env-file "${COMPOSE_ENV}" --profile 'build' build
|
||||||
|
```
|
||||||
|
{%- endif %}
|
||||||
|
|
||||||
|
## Start
|
||||||
|
|
||||||
|
```
|
||||||
|
{%- if ',' in cookiecutter.__component_list_slug %}
|
||||||
|
docker compose --project-name "${COMPOSE_PROJECT}" --file "${COMPOSE_FILE}" --env-file "${COMPOSE_ENV}" --profile 'full' up --detach
|
||||||
|
{%- else %}
|
||||||
|
docker compose --project-name "${COMPOSE_PROJECT}" --file "${COMPOSE_FILE}" --env-file "${COMPOSE_ENV}" up --detach
|
||||||
|
{%- endif %}
|
||||||
|
```
|
||||||
|
|
||||||
|
# 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
|
||||||
|
```
|
||||||
|
{%- if ',' in cookiecutter.__component_list_slug -%}
|
||||||
|
{%- set components = cookiecutter.__component_list_slug.split(',') -%}
|
||||||
|
{%- for component in components %}
|
||||||
|
zfs create -p 'zpool/docker-data/{{ cookiecutter.__service_slug }}-${COMPOSE_CTX}/{{ component }}/data/db'
|
||||||
|
zfs create -p 'zpool/docker-data/{{ cookiecutter.__service_slug }}-${COMPOSE_CTX}/{{ component }}/data/logs'
|
||||||
|
zfs create -p 'zpool/docker-data/{{ cookiecutter.__service_slug }}-${COMPOSE_CTX}/{{ component }}/config'
|
||||||
|
{%- endfor -%}
|
||||||
|
{%- else %}
|
||||||
|
zfs create -p 'zpool/docker-data/{{ cookiecutter.__service_slug }}-${COMPOSE_CTX}/{{ cookiecutter.__service_slug }}/data/db'
|
||||||
|
zfs create -p 'zpool/docker-data/{{ cookiecutter.__service_slug }}-${COMPOSE_CTX}/{{ cookiecutter.__service_slug }}/data/logs'
|
||||||
|
zfs create -p 'zpool/docker-data/{{ cookiecutter.__service_slug }}-${COMPOSE_CTX}/{{ cookiecutter.__service_slug }}/config'
|
||||||
|
{%- endif %}
|
||||||
|
```
|
||||||
|
When changing bind mount locations to real ones remember to also update `volumes:` in [docker-compose.yml](docker-compose.yml).
|
||||||
|
|
||||||
|
* Change ownership
|
||||||
|
```
|
||||||
|
{%- set components = cookiecutter.__component_list_slug.split(',') -%}
|
||||||
|
{% for component in components %}
|
||||||
|
{%- if loop.first %}
|
||||||
|
chown -R 1000:1000 '/opt/docker-data/{{ cookiecutter.__service_slug }}-${COMPOSE_CTX}/{{ cookiecutter.__service_slug }}/data'
|
||||||
|
{%- endif %}
|
||||||
|
{%- endfor %}
|
||||||
|
```
|
||||||
|
|
||||||
|
When done head back up to [How to run](#how-to-run).
|
@ -1,6 +1,15 @@
|
|||||||
services:
|
services:
|
||||||
{%- if ',' in cookiecutter.__component_list_slug -%}
|
{%- if ',' in cookiecutter.__component_list_slug -%}
|
||||||
{%- set components = cookiecutter.__component_list_slug.split(',') -%}
|
{%- set components = cookiecutter.__component_list_slug.split(',') -%}
|
||||||
|
{%- set ns = namespace(found=false) -%}
|
||||||
|
{%- for component in components %}
|
||||||
|
{%- if loop.first -%}
|
||||||
|
{%- set ns.first_component = component -%}
|
||||||
|
{%- elif N is undefined -%}
|
||||||
|
{%- set ns.second_component = component -%}
|
||||||
|
{%- set N = 0 -%}
|
||||||
|
{%- endif -%}
|
||||||
|
{%- endfor -%}
|
||||||
{%- for component in components %}
|
{%- for component in components %}
|
||||||
{{ component }}:
|
{{ component }}:
|
||||||
image: "{{ component }}:${% raw %}{{% endraw %}{{ component.upper() }}_VERSION{% raw %}}{% endraw %}"
|
image: "{{ component }}:${% raw %}{{% endraw %}{{ component.upper() }}_VERSION{% raw %}}{% endraw %}"
|
||||||
@ -8,15 +17,29 @@ services:
|
|||||||
networks:
|
networks:
|
||||||
{{ cookiecutter.__service_slug }}-default:
|
{{ cookiecutter.__service_slug }}-default:
|
||||||
profiles: ["full", "{{ component }}"]
|
profiles: ["full", "{{ component }}"]
|
||||||
|
{% if loop.first -%}
|
||||||
|
depends_on:
|
||||||
|
{{ ns.second_component }}:
|
||||||
|
condition: service_healthy
|
||||||
|
{%- else -%}
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "fping", "--count=1", "${% raw %}{{% endraw %}{{ ns.first_component.upper() }}_VIP{% raw %}}{% endraw %}", "--period=500", "--quiet"]
|
||||||
|
interval: 3s
|
||||||
|
timeout: 1s
|
||||||
|
retries: 60
|
||||||
|
start_period: 2s
|
||||||
|
{%- endif %}
|
||||||
extends:
|
extends:
|
||||||
file: common-settings.yml
|
file: common-settings.yml
|
||||||
service: common-settings
|
service: common-settings
|
||||||
ports:
|
ports:
|
||||||
# - "8080:80"
|
# - "8080:80"
|
||||||
volumes:
|
volumes:
|
||||||
# - /opt/docker-data/{{ cookiecutter.__service_slug }}-{{ component }}-${CONTEXT}/{{ component }}/data/db:/usr/lib/{{ component }}
|
# When changing bind mount locations to real ones remember to
|
||||||
# - /opt/docker-data/{{ cookiecutter.__service_slug }}-{{ component }}-${CONTEXT}/{{ component }}/data/logs:/var/log/{{ component }}
|
# also update "Initial setup" section in README.md.
|
||||||
# - /opt/docker-data/{{ cookiecutter.__service_slug }}-{{ component }}-${CONTEXT}/{{ component }}/config:/etc/{{ component }}
|
# - /opt/docker-data/{{ cookiecutter.__service_slug }}-${CONTEXT}/{{ component }}/data/db:/usr/lib/{{ component }}
|
||||||
|
# - /opt/docker-data/{{ cookiecutter.__service_slug }}-${CONTEXT}/{{ component }}/data/logs:/var/log/{{ component }}
|
||||||
|
# - /opt/docker-data/{{ cookiecutter.__service_slug }}-${CONTEXT}/{{ component }}/config:/etc/{{ component }}
|
||||||
environment:
|
environment:
|
||||||
# {{ component.upper() }}_USER: ${% raw %}{{% endraw %}{{ component.upper() }}_USER{% raw %}}{% endraw %}
|
# {{ component.upper() }}_USER: ${% raw %}{{% endraw %}{{ component.upper() }}_USER{% raw %}}{% endraw %}
|
||||||
# {{ component.upper() }}_PASSWORD: ${% raw %}{{% endraw %}{{ component.upper() }}_PASSWORD{% raw %}}{% endraw %}
|
# {{ component.upper() }}_PASSWORD: ${% raw %}{{% endraw %}{{ component.upper() }}_PASSWORD{% raw %}}{% endraw %}
|
||||||
@ -33,9 +56,11 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
# - "8080:80"
|
# - "8080:80"
|
||||||
volumes:
|
volumes:
|
||||||
# - /opt/docker-data/{{ cookiecutter.__service_slug }}-${CONTEXT}/data/db:/usr/lib/{{ cookiecutter.__service_slug }}
|
# When changing bind mount locations to real ones remember to
|
||||||
# - /opt/docker-data/{{ cookiecutter.__service_slug }}-${CONTEXT}/data/logs:/var/log/{{ cookiecutter.__service_slug }}
|
# also update "Initial setup" section in README.md.
|
||||||
# - /opt/docker-data/{{ cookiecutter.__service_slug }}-${CONTEXT}/config:/etc/{{ cookiecutter.__service_slug }}
|
# - /opt/docker-data/{{ cookiecutter.__service_slug }}-${CONTEXT}/{{ cookiecutter.__service_slug }}/data/db:/usr/lib/{{ cookiecutter.__service_slug }}
|
||||||
|
# - /opt/docker-data/{{ cookiecutter.__service_slug }}-${CONTEXT}/{{ cookiecutter.__service_slug }}/data/logs:/var/log/{{ cookiecutter.__service_slug }}
|
||||||
|
# - /opt/docker-data/{{ cookiecutter.__service_slug }}-${CONTEXT}/{{ cookiecutter.__service_slug }}/config:/etc/{{ cookiecutter.__service_slug }}
|
||||||
environment:
|
environment:
|
||||||
# {{ cookiecutter.__component_list_slug.upper() }}_USER: ${% raw %}{{% endraw %}{{ cookiecutter.__component_list_slug.upper() }}_USER{% raw %}}{% endraw %}
|
# {{ cookiecutter.__component_list_slug.upper() }}_USER: ${% raw %}{{% endraw %}{{ cookiecutter.__component_list_slug.upper() }}_USER{% raw %}}{% endraw %}
|
||||||
# {{ cookiecutter.__component_list_slug.upper() }}_PASSWORD: ${% raw %}{{% endraw %}{{ cookiecutter.__component_list_slug.upper() }}_PASSWORD{% raw %}}{% endraw %}
|
# {{ cookiecutter.__component_list_slug.upper() }}_PASSWORD: ${% raw %}{{% endraw %}{{ cookiecutter.__component_list_slug.upper() }}_PASSWORD{% raw %}}{% endraw %}
|
||||||
@ -49,4 +74,4 @@ networks:
|
|||||||
ipam:
|
ipam:
|
||||||
driver: default
|
driver: default
|
||||||
config:
|
config:
|
||||||
# - subnet: 172.21.184.0/24
|
- subnet: ${SUBNET}
|
||||||
|
@ -8,6 +8,11 @@ CONTEXT={{ cookiecutter.__context_slug }}
|
|||||||
{% for component in components %}
|
{% for component in components %}
|
||||||
# {{ component.upper() }}_VERSION=x.y.z
|
# {{ component.upper() }}_VERSION=x.y.z
|
||||||
{%- endfor %}
|
{%- endfor %}
|
||||||
|
{%- for component in components %}
|
||||||
|
{%- if loop.first %}
|
||||||
|
# {{ component.upper() }}_VIP=10.1.1.2
|
||||||
|
{%- endif %}
|
||||||
|
{%- endfor %}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -26,6 +31,16 @@ CONTEXT={{ cookiecutter.__context_slug }}
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# 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
|
# See 'docker-compose.override.yml' for how to make a variable available in
|
||||||
# a Dockerfile
|
# a Dockerfile
|
||||||
# ---
|
# ---
|
Loading…
x
Reference in New Issue
Block a user