Skip to content
This repository was archived by the owner on Nov 8, 2019. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 39 additions & 11 deletions mysql/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,19 +1,47 @@
FROM fedora
MAINTAINER http://fedoraproject.org/wiki/Cloud

RUN dnf -y update && dnf clean all
RUN dnf -y install community-mysql-server community-mysql pwgen supervisor bash-completion psmisc net-tools && dnf clean all
# MySQL image for OpenShift.
#
# Volumes:
# * /var/lib/mysql/data - Datastore for MySQL
# Environment:
# * $MYSQL_USER - Database user name
# * $MYSQL_PASSWORD - User's password
# * $MYSQL_DATABASE - Name of the database to create
# * $MYSQL_ROOT_PASSWORD (Optional) - Password for the 'root' MySQL account

ADD ./start.sh /start.sh
ADD ./config_mysql.sh /config_mysql.sh
ADD ./supervisord.conf /etc/supervisord.conf
MAINTAINER http://fedoraproject.org/wiki/Cloud

# RUN echo %sudo ALL=NOPASSWD: ALL >> /etc/sudoers
ENV MYSQL_VERSION=10.0 \
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

5.6? But see below.

HOME=/var/lib/mysql

RUN chmod 755 /start.sh
RUN chmod 755 /config_mysql.sh
RUN /config_mysql.sh
LABEL io.k8s.description="MySQL is a multi-user, multi-threaded SQL database server" \
io.k8s.display-name="MySQL 5.6" \
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think 5.6 is correct either, because unlike with the SCL, you're not ensuring the version. I would drop the version numbers. These dockerfiles aren't for a specific version; they're for whatever is in the Fedora package. Maybe you'd want to write it as "Fedora MySQL" or "Fedora 23 MySQL" instead, I dunno.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, this is a remnant from the time when there were branches, so we could distinguish versions. I still think that version is important though, since users need to think differently in some aspects when working with 5.6 or 5.7.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe a test in the Dockerfile would be useful then, to ensure the description did not become misleading.

io.openshift.expose-services="3306:mysql" \
io.openshift.tags="database,mysql,mysql56"

EXPOSE 3306

CMD ["/bin/bash", "/start.sh"]
# This image must forever use UID 27 for mysql user so our volumes are
# safe in the future. This should *never* change, the last test is there
# to make sure of that.
RUN dnf -y --setopt=tsflags=nodocs install gettext hostname bind-utils community-mysql-server community-mysql && \
dnf clean all && \
mkdir -p /var/lib/mysql/data && chown -R mysql.0 /var/lib/mysql && \
test "$(id mysql)" = "uid=27(mysql) gid=27(mysql) groups=27(mysql)"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I've seen elsewhere - including Docker docs - is creating the user in advance. It's not hard to specify the UID, and it'll error out if there's a conflict. And it seemed to work ok in my image? The comment doesn't explain why it's done differently here, anyway.

Personally, I find the other way more obvious. Seeing the naked assertion "Fedora creates mysql with the UID 27" is surprising. Unless we want to reference an allocation document. I came across the Debian allocations when I was looking into this, hopefully there's a Fedora equivalent. As I say, my understanding is these images are nice examples people can learn and derive from, so uncommented magic numbers are not the best.

It might also help avoid having different boilerplate, dependent on whether the ID is dynamic or not...

I guess if we specified the ID, we still have to choose between referencing the allocation document, or changing it to something like 1000.

I think the nice thing about distro hard-coded ID allocations is for rescue disks. On Docker I'm not sure there's any reason not to allocate our own IDs. I guess it is a low-level interference with the Fedora package though ("layering violation").

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

27 is what mysql user uses outside of containers world, so it makes sense to me to use the same UID as default one for data created by container as well.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right. But how does the reader know whether 27 was a dynamically allocated id - which would be fragile and ugly - or statically allocated by Fedora? I only know because I looked into the ranges for a different reason (commit message e8f5980).


COPY run-*.sh /usr/local/bin/
COPY contrib /var/lib/mysql/

# Loosen permission bits for group to avoid problems running container with
# arbitrary UID
# When only specifying user, group is 0, that's why /var/lib/mysql must have
# owner mysql.0; that allows to avoid a+rwx for this dir
RUN chmod -R g+rwx /var/lib/mysql

VOLUME ["/var/lib/mysql/data"]

USER 27

ENTRYPOINT ["run-mysqld.sh"]
CMD ["mysqld"]
93 changes: 70 additions & 23 deletions mysql/README.md
Original file line number Diff line number Diff line change
@@ -1,41 +1,88 @@
dockerfiles-fedora-MySQL
========================
MySQL for general usage and OpenShift - Docker image
======================================================

This repo contains a recipe for making Docker container for SSH and MySQL on Fedora.
This repository contains Dockerfiles for MySQL images for general usage and for OpenShift.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dockerfile without the 's' here, I think. Technically this directory isn't a repository of its own either.

Maybe "This directory contains a Dockerfile to build MySQL images..."

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, copy&paste error.. needs fixing.

Check your Docker version

# docker version
Environment variables and volumes
----------------------------------

Perform the build
The image recognizes the following environment variables that you can set during
initialization by passing `-e VAR=VALUE` to the Docker run command.

# docker build --rm -t <yourname>/mysql .
| Variable name | Description |
| :--------------------- | ----------------------------------------- |
| `MYSQL_USER` | User name for MySQL account to be created |
| `MYSQL_PASSWORD` | Password for the user account |
| `MYSQL_DATABASE` | Database name |
| `MYSQL_ROOT_PASSWORD` | Password for the root user (optional) |

Check the image out.
The following environment variables influence the MySQL configuration file. They are all optional.

# docker images
| Variable name | Description | Default
| :------------------------------ | ----------------------------------------------------------------- | -------------------------------
| `MYSQL_LOWER_CASE_TABLE_NAMES` | Sets how the table names are stored and compared | 0
| `MYSQL_MAX_CONNECTIONS` | The maximum permitted number of simultaneous client connections | 151
| `MYSQL_FT_MIN_WORD_LEN` | The minimum length of the word to be included in a FULLTEXT index | 4
| `MYSQL_FT_MAX_WORD_LEN` | The maximum length of the word to be included in a FULLTEXT index | 20
| `MYSQL_AIO` | Controls the `innodb_use_native_aio` setting value in case the native AIO is broken. See http://help.directadmin.com/item.php?id=529 | 1

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So... I didn't particularly like this. The interface isn't too bad, but I can't help thinking it's one of the reasons the image ends up being so much more complicated. (I saw that diffstat :-P).

This doesn't particularly sound like what you'd do for a "set of examples that can assist in learning about Docker" etc. My opinion - without having much experience - was that users should derive a Dockerfile with their desired config files.

There's an LWN post about the Kubernetes 1.2 release which makes a related point. It mentions a new ConfigMap feature which injects config files at runtime. http://lwn.net/Articles/680833/

Maybe the AIO thing is important enough it needs to be kept? In which case it would be nice to have an example scenario where it's needed. I'm imagining "Some Docker hosting services are known to require this", or "Some container hosting services are known to require this" (euphemism for VPS) if we can't provide anything more specific. I.e. you might end up needing it when deploying, but it shouldn't be needed on Fedora, right?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm fine with removing these specific options for Fedora and have some general way to configure the daemon.

Run it:
You can also set the following mount points by passing the `-v /host:/container` flag to Docker.

# docker run -d -p 3306:3306 <yourname>/mysql
| Volume mount point | Description |
| :----------------------- | -------------------- |
| `/var/lib/mysql/data` | MySQL data directory |

Get container ID:
**Notice: When mouting a directory from the host into the container, ensure that the mounted
directory has the appropriate permissions and that the owner and group of the directory
matches the user UID or name which is running inside the container.**
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

name?? just the ID, I should think.


# docker ps
Usage
---------------------------------

Keep in mind the password set for MySQL is: mysqlPassword
For this, we will assume that you are using the `openshift/mysql-55-centos7` image.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line not required, I think (only one mysql image here)

If you want to set only the mandatory environment variables and not store
the database in a host directory, execute the following command:

Get the IP address for the container:
```
$ docker run -d --name mysql_database -e MYSQL_USER=user -e MYSQL_PASSWORD=pass -e MYSQL_DATABASE=db -p 3306:3306 fedora/mysql
```

# docker inspect <container_id> | grep -i ipaddr
This will create a container named `mysql_database` running MySQL with database
`db` and user with credentials `user:pass`. Port 3306 will be exposed and mapped
to the host. If you want your database to be persistent across container executions,
also add a `-v /host/db/path:/var/lib/mysql/data` argument. This will be the MySQL
data directory.

For MySQL:
# mysql -h 172.17.0.x -utestdb -pmysqlPassword
If the database directory is not initialized, the entrypoint script will first
run [`mysql_install_db`](https://dev.mysql.com/doc/refman/5.6/en/mysql-install-db.html)
and setup necessary database users and passwords. After the database is initialized,
or if it was already present, `mysqld` is executed and will run as PID 1. You can
stop the detached container by running `docker stop mysql_database`.


Create a table:
MySQL root user
---------------------------------
The root user has no password set by default, only allowing local connections.
You can set it by setting the `MYSQL_ROOT_PASSWORD` environment variable. This
will allow you to login to the root account remotely. Local connections will
still not require a password.

To disable remote root access, simply unset `MYSQL_ROOT_PASSWORD` and restart
the container.


Changing passwords
------------------

Since passwords are part of the image configuration, the only supported method
to change passwords for the database user (`MYSQL_USER`) and root user is by
changing the environment variables `MYSQL_PASSWORD` and `MYSQL_ROOT_PASSWORD`,
respectively.

Changing database passwords through SQL statements or any way other than through
the environment variables aforementioned will cause a mismatch between the
values stored in the variables and the actual passwords. Whenever a database
container starts it will reset the passwords to the values stored in the
environment variables.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to be one of the other reasons driving complexity - the need to update the password of an already existing image.

Why do we have a password at all?

My thought was that it might be necessary in order to work with software which is opinionated or just buggy. (Scripts that end up running something like mysql -uroot -p "" won't work; I think you have to use --password=).

But in that case I don't think you need to be able to change the password You just initialize it to the simplest placeholder that the opinionated or buggy software will tolerate.

```
\> CREATE TABLE test (name VARCHAR(10), owner VARCHAR(10),
-> species VARCHAR(10), birth DATE, death DATE);
```
29 changes: 0 additions & 29 deletions mysql/config_mysql.sh

This file was deleted.

165 changes: 165 additions & 0 deletions mysql/contrib/common.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
#!/bin/bash

# Data directory where MySQL database files live. The data subdirectory is here
# because .bashrc and my.cnf both live in /var/lib/mysql/ and we don't want a
# volume to override it.
export MYSQL_DATADIR=/var/lib/mysql/data

# Configuration settings.
export MYSQL_DEFAULTS_FILE=$HOME/my.cnf
export MYSQL_LOWER_CASE_TABLE_NAMES=${MYSQL_LOWER_CASE_TABLE_NAMES:-0}
export MYSQL_MAX_CONNECTIONS=${MYSQL_MAX_CONNECTIONS:-151}
export MYSQL_FT_MIN_WORD_LEN=${MYSQL_FT_MIN_WORD_LEN:-4}
export MYSQL_FT_MAX_WORD_LEN=${MYSQL_FT_MAX_WORD_LEN:-20}
export MYSQL_AIO=${MYSQL_AIO:-1}

# Be paranoid and stricter than we should be.
# https://dev.mysql.com/doc/refman/5.6/en/identifiers.html
mysql_identifier_regex='^[a-zA-Z0-9_]+$'
mysql_password_regex='^[a-zA-Z0-9_~!@#$%^&*()-=<>,.?;:|]+$'

function usage() {
[ $# == 2 ] && echo "error: $1"
echo "You must either specify the following environment variables:"
echo " MYSQL_USER (regex: '$mysql_identifier_regex')"
echo " MYSQL_PASSWORD (regex: '$mysql_password_regex')"
echo " MYSQL_DATABASE (regex: '$mysql_identifier_regex')"
echo "Or the following environment variable:"
echo " MYSQL_ROOT_PASSWORD (regex: '$mysql_password_regex')"
echo "Or both."
echo "Optional Settings:"
echo " MYSQL_LOWER_CASE_TABLE_NAMES (default: 0)"
echo " MYSQL_MAX_CONNECTIONS (default: 151)"
echo " MYSQL_FT_MIN_WORD_LEN (default: 4)"
echo " MYSQL_FT_MAX_WORD_LEN (default: 20)"
echo " MYSQL_AIO (default: 1)"
exit 1
}

function validate_variables() {
# Check basic sanity of specified variables
if [[ -v MYSQL_USER && -v MYSQL_PASSWORD && -v MYSQL_DATABASE ]]; then
[[ "$MYSQL_USER" =~ $mysql_identifier_regex ]] || usage "Invalid MySQL username"
[ ${#MYSQL_USER} -le 16 ] || usage "MySQL username too long (maximum 16 characters)"
[[ "$MYSQL_PASSWORD" =~ $mysql_password_regex ]] || usage "Invalid password"
[[ "$MYSQL_DATABASE" =~ $mysql_identifier_regex ]] || usage "Invalid database name"
[ ${#MYSQL_DATABASE} -le 64 ] || usage "Database name too long (maximum 64 characters)"
user_specified=1
fi

if [ -v MYSQL_ROOT_PASSWORD ]; then
[[ "$MYSQL_ROOT_PASSWORD" =~ $mysql_password_regex ]] || usage "Invalid root password"
root_specified=1
fi

# Either combination of user/pass/db or root password is ok
if [[ "${user_specified:-0}" == "0" && "${root_specified:-0}" == "0" ]]; then
usage
fi

# Specifically check of incomplete specification
if [[ -v MYSQL_USER || -v MYSQL_PASSWORD || -v MYSQL_DATABASE ]] && \
[[ "${user_specified:-0}" == "0" ]]; then
usage
fi
}

# Make sure env variables don't propagate to mysqld process.
function unset_env_vars() {
unset MYSQL_USER MYSQL_PASSWORD MYSQL_DATABASE MYSQL_ROOT_PASSWORD
}

# Poll until MySQL responds to our ping.
function wait_for_mysql() {
pid=$1 ; shift

while [ true ]; do
if [ -d "/proc/$pid" ]; then
mysqladmin --socket=/tmp/mysql.sock ping &>/dev/null && return 0
else
return 1
fi
echo "Waiting for MySQL to start ..."
sleep 1
done
}

function start_local_mysql() {
# Now start mysqld and add appropriate users.
echo 'Starting local mysqld server ...'
/usr/libexec/mysqld \
--defaults-file=$MYSQL_DEFAULTS_FILE \
--skip-networking --socket=/tmp/mysql.sock "$@" &
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

--skip-networking is an excellent point (grr mysql, why no "run this sql and then exit option). But why do we need an explicit --socket=?

mysql_pid=$!
wait_for_mysql $mysql_pid
}

# Initialize the MySQL database (create user accounts and the initial database)
function initialize_database() {
echo 'Running mysql_install_db ...'
# Using --rpm since we need mysql_install_db behaves as in RPM
mysql_install_db --rpm --datadir=$MYSQL_DATADIR
start_local_mysql "$@"

[ -v MYSQL_RUNNING_AS_SLAVE ] && return

# Do not care what option is compulsory here, just create what is specified
if [ -v MYSQL_USER ]; then
mysql $mysql_flags <<EOSQL
CREATE USER '${MYSQL_USER}'@'%' IDENTIFIED BY '${MYSQL_PASSWORD}';
EOSQL
fi

if [ -v MYSQL_DATABASE ]; then
mysqladmin $admin_flags create "${MYSQL_DATABASE}"

if [ -v MYSQL_USER ]; then
mysql $mysql_flags <<EOSQL
GRANT ALL ON \`${MYSQL_DATABASE}\`.* TO '${MYSQL_USER}'@'%' ;
FLUSH PRIVILEGES ;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First comment I originally intended to make, to improve this - FLUSH PRIVILEGES should not be necessary here. According to rant: http://dbahire.com/stop-using-flush-privileges/

EOSQL
fi
fi

if [ -v MYSQL_ROOT_PASSWORD ]; then
mysql $mysql_flags <<EOSQL
GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY '${MYSQL_ROOT_PASSWORD}';
EOSQL
fi
}

# The 'server_id' number for slave needs to be within 1-4294967295 range.
# This function will take the 'hostname' if the container, hash it and turn it
# into the number.
# See: https://dev.mysql.com/doc/refman/5.6/en/replication-options.html#option_mysqld_server-id
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the plan for replication? Looks like there's still a lot of code here, but I can't find the examples/replica directory that explained how to use it.

function server_id() {
checksum=$(sha256sum <<< $(hostname -i))
checksum=${checksum:0:14}
echo -n $((0x${checksum}%4294967295))
}

function wait_for_mysql_master() {
while true; do
echo "Waiting for MySQL master (${MYSQL_MASTER_SERVICE_NAME}) to accept connections ..."
mysqladmin --host=${MYSQL_MASTER_SERVICE_NAME} --user="${MYSQL_MASTER_USER}" \
--password="${MYSQL_MASTER_PASSWORD}" ping &>/dev/null && return 0
sleep 1
done
}

function validate_replication_variables() {
if ! [[ -v MYSQL_DATABASE && -v MYSQL_MASTER_USER && -v MYSQL_MASTER_PASSWORD && \
( "${MYSQL_RUNNING_AS_SLAVE:-0}" != "1" || -v MYSQL_MASTER_SERVICE_NAME ) ]]; then
echo
echo "For master/slave replication, you have to specify following environment variables:"
echo " MYSQL_MASTER_SERVICE_NAME (slave only)"
echo " MYSQL_DATABASE"
echo " MYSQL_MASTER_USER"
echo " MYSQL_MASTER_PASSWORD"
echo
fi
[[ "$MYSQL_DATABASE" =~ $mysql_identifier_regex ]] || usage "Invalid database name"
[[ "$MYSQL_MASTER_USER" =~ $mysql_identifier_regex ]] || usage "Invalid MySQL master username"
[ ${#MYSQL_MASTER_USER} -le 16 ] || usage "MySQL master username too long (maximum 16 characters)"
[[ "$MYSQL_MASTER_PASSWORD" =~ $mysql_password_regex ]] || usage "Invalid MySQL master password"
}
12 changes: 12 additions & 0 deletions mysql/contrib/my-master.cnf.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[mysqld]

server-id = ${MYSQL_SERVER_ID}
log_bin = ${MYSQL_DATADIR}/mysql-bin.log
binlog_do_db = mysql
binlog_do_db = ${MYSQL_DATABASE}
gtid_mode = ON
log-slave-updates = ON
enforce-gtid-consistency = ON

# Include the common MySQL settings
!include ${HOME}/my-common.cnf
13 changes: 13 additions & 0 deletions mysql/contrib/my-slave.cnf.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[mysqld]

server-id = ${MYSQL_SERVER_ID}
log_bin = ${MYSQL_DATADIR}/mysql-bin.log
relay-log = ${MYSQL_DATADIR}/mysql-relay-bin.log
binlog_do_db = mysql
binlog_do_db = ${MYSQL_DATABASE}
gtid_mode = ON
log-slave-updates = ON
enforce-gtid-consistency = ON

# Include the common MySQL settings
!include ${HOME}/my-common.cnf
Loading