Feature/external deployment replication#109
Conversation
|
Thanks! I will try to take a look a this and provide some feedback by Monday. |
1f0db8a to
a83d48a
Compare
abg
left a comment
There was a problem hiding this comment.
I reviewed this and left a trail of comments.
Beyond the concerns already noted, I would like to see some answer for Galera <-> Galera replication - even if that's simply "replica must only be a single node for now". I.e. I think it would be fine for a template in the pxc-replicator job to abort if the numbers of MySQL nodes != 1. Robustly handling cluster to cluster async replication could be interesting too, but would be much more work.
I would also encourage pxc-replicator to be written in a Go module that is more robust and easier to test and maintain than the current bash scripting.
| log "resyncing with full backup" | ||
| log $($self_mysql <<< "STOP REPLICA;") | ||
| log $($self_mysql <<< "RESET REPLICA;") | ||
| log $($self_mysql <<< "RESET MASTER;") |
There was a problem hiding this comment.
Just a note: RESET MASTER is deprecated in v8.4 and removed in MySQL v9, replaced with RESET BINARY LOGS AND GTIDS.
I understand this is likely used here since pxc-release still supports MySQL v8.0 which still requires this syntax.
There was a problem hiding this comment.
yeah, what's the preferred way of handling this?
I noticed while reading docs that they're still cleaning up the weirdo naming..
I tried already using the replica/source naming as far as possible but didn't know if it would be idempotent :/
https://dev.mysql.com/doc/refman/8.4/en/replication-howto-repuser.html
it seems that the permissions still does not have an alternative?
There was a problem hiding this comment.
Yeah, v8.0 (and earlier) only support RESET MASTER and v8.4 only supports RESET BINARY LOGS ...
We've typically dealt with this kind of thing with version checks:
if mysql_major_minor() == "8.0": # <- Delete branch when we don't care about old version anymore
do_legacy_sql_stuff()
else:
do_latest_sql_stuff()
endThat could potentially be done at template rendering time by looking at the p('mysql_version') property.
MySQL v8.0 is EOL (although we'll probably get one more patch release from Percona for v8.0.46), so there is an argument to only support v8.4 and onwards. Maybe guard against using this feature on v8.0?
# In some pxc-replicator template
<%- if p('mysql_version') == "8.0" -%>
raise "Unsupported <helpful error message>"
<%- end -%>There was a problem hiding this comment.
hmm. How'd that play with cf-depl?
It seems that 8.4 is still experimental cloudfoundry/cf-deployment@3870c60
I'm not sure if there are any reasons why it's still setup to default to 8.0 outside of being cautious.
I'm going to ask around what the plans for runtime are in regards to switching the default. For now I guess I'll try to find all relevant places and sprinkle some ifs
There was a problem hiding this comment.
My thinking here was that it could be reasonable to not support this replication feature for EOL MySQL versions to simplify things.
However this exposes that, as pxc-release maintainers, we have been bad netizens of cf-d and have not updated the default MySQL version to v8.4. I'll try to follow-up on that.
I think it's okay to add a version check to support both the old and new syntax for now.
|
|
||
| while [[ $SECONDS -lt <%= p('monit_startup_timeout') %> ]]; do | ||
| if grep "$(date +%Y-%m-%dT%H:%M).*replication healthy" /var/vcap/sys/log/pxc-replicator/pxc-replicator.stdout.log > /dev/null; then | ||
| log "replication reported healthy within the laset minute" |
| SECONDS=0 | ||
|
|
||
| while [[ $SECONDS -lt <%= p('monit_startup_timeout') %> ]]; do | ||
| if grep "$(date +%Y-%m-%dT%H:%M).*replication healthy" /var/vcap/sys/log/pxc-replicator/pxc-replicator.stdout.log > /dev/null; then |
There was a problem hiding this comment.
This seems a little brittle. I would probably actually query the replica and validate the IO_Thread and SQL_Thread are in a healthy state.
|
first of all, sorry for the horrible PR ( I'm going to excuse myself by having had to learn a lot about pxc and mysql) and second thank you for the in depth review. I marked this as draft for now. I set up ci to deploy the current state of the release and iterate on it until this is ready. As far as: should this be go code? Yes :). I think I misunderstood you when I asked in slack about There's one thing that I already can answer, my use case is definitely running an external single instance replica to offload backups from a cf-deployment cluster. I'd like to avoid trying to create the My suggestion would be this, I'd like to keep the Again, thank you for the feedback. |
Yep, apologies for any confusing feedback. Some of my comments were "nice-to-have-for-the-future" and I didn't make that super obvious. I wouldn't expect a perfect implementation in this initial PR. Targeting just cf-deployment use cases, mysqldump is probably fine and a single replica is fine. I think requiring TLS for the MySQL replication channel (and mysqldump / provisioning channel) would be ideal - and at least TLS being a supported configuration. However, mutual TLS and other advanced use cases are not necessary.
Yep, I think that's fine for now. A Go implementation doesn't need to be part of this PR, but I think it's a good end state for testability and alignment with other tooling in the release. |
0fd7129 to
02c0ae7
Compare
|
@abg I think I addressed all of the comments, let me know if the current setup is any better. |
abg
left a comment
There was a problem hiding this comment.
Apologies for the slow feedback. This is improved, but not deployable yet.
I've left some more feedback.
|
|
||
|
|
||
| <%- if_p('tls.client.ca','tls.client.certificate','tls.client.private_key') do -%> | ||
| SOURCE_SSL_CONFIG="SOURCE_SSL_CA = '/var/vcap/jobs/pxc-replicator/certificates/server-ca.pem', SOURCE_SSL_CERT = '/var/vcap/jobs/pxc-replicator/certificates/client-cert.pem', SOURCE_SSL_KEY = '/var/vcap/jobs/pxc-replicator/certificates/client-key.pem'" |
There was a problem hiding this comment.
I didn't think deeply about this during my initial review pass so up-front apologies for not surfacing this earlier.
These certificates are going to be read by the mysqld process running in the pxc-mysql bpm context. That job currently would not have access to the pxc-replicator job certificates without adjusting the bpm.yml.
Rather than doing this, I think maybe just configure certificates on the pxc-mysql job. This should be the point of the pxc-mysql tls.client.{certificate,key} properties. If configured then you can just set SOURCE_SSL_{CERT,KEY} = /var/vcap/jobs/pxc-mysql/certificates/client-{cert,key}.pem
There was a problem hiding this comment.
SOURCE_SSL_{CERT,KEY} is only needed if you want x509 authentication (mTLS).
Not necessarily bad a security perspective but the replication user would need to be specifically configured for it.
i.e.
CREATE USER $replicationUSER REQUIRE X509 .... ;
-- or
CREATE USER $replicationUSER REQUIRE SUBJECT '/CN=some-client-identity' ...;As I mentioned in an earlier comment, mutual TLS is not a requirement for an MVP but maybe a nice-to-have. If you need it, it would add it as an option to user creation.
| log "resyncing with full backup" | ||
| log $($self_mysql <<< "STOP REPLICA;") | ||
| log $($self_mysql <<< "RESET REPLICA;") | ||
| log $($self_mysql <<< "RESET MASTER;") |
There was a problem hiding this comment.
My thinking here was that it could be reasonable to not support this replication feature for EOL MySQL versions to simplify things.
However this exposes that, as pxc-release maintainers, we have been bad netizens of cf-d and have not updated the default MySQL version to v8.4. I'll try to follow-up on that.
I think it's okay to add a version check to support both the old and new syntax for now.
| args: [] | ||
| env: | ||
| PATH: <%= path.join(":") %> | ||
| <%- if_p('mysql_backup_username', 'mysql_backup_password' ,'host', 'port') do |user,pass,host| -%> |
There was a problem hiding this comment.
Ideally we would apply the principal of least privilege here. Otherwise this will get flagged by security scans.
Replication doesn't need full SELECT access on tables from the primary - it just needs the privilege to stream binary logs.
Similarly the backup user just needs minimal privileges for a backup, but not necessarily binlog streaming privileges.
|
|
||
|
|
||
| <%- if_p('tls.client.ca','tls.client.certificate','tls.client.private_key') do -%> | ||
| SOURCE_SSL_CONFIG="SOURCE_SSL_CA = '/var/vcap/jobs/pxc-replicator/certificates/server-ca.pem', SOURCE_SSL_CERT = '/var/vcap/jobs/pxc-replicator/certificates/client-cert.pem', SOURCE_SSL_KEY = '/var/vcap/jobs/pxc-replicator/certificates/client-key.pem'" |
There was a problem hiding this comment.
Worth noting that best practice would also set SOURCE_SSL_VERIFY_SERVER_CERT (hostname verification).
This would require the primary's TLS certificate to contain the bosh dns server name (provided by the link here) Bosh docs.
In the context of cf-d this is usually sql-db.service.cf.internal but would require explicit configuration.
| ] | ||
| flags = "" | ||
| if_p('tls.client.ca','tls.client.certificate','tls.client.private_key') do | ||
| flags = "--ssl-ca=/var/vcap/jobs/pxc-replicator/certificates/server-ca.pem --ssl-cert=/var/vcap/jobs/pxc-replicator/certificates/client-cert.pem --ssl-key=/var/vcap/jobs/pxc-replicator/certificates/client-key.pem" |
There was a problem hiding this comment.
Any reason not to set these flags via the my.cnf? (i.e. source{,.admin}.mysql.cnf)
| optional: true | ||
|
|
||
| provides: | ||
| - name: source |
There was a problem hiding this comment.
Could we reuse the mysql link and just expose more properties on it? It already has port and mysql_version
If we are pulling all this from the "primary" deployments link anyway, I was imagining something like:
# primary deployment manifest
- instance_groups:
- name: mysql
jobs:
- name: pxc-mysql
...
provides: mysql: {as: mysql, shared: true}
# replica deployment manifest
- instance_groups:
- name: mysql
jobs:
- name: pxc-replicator
...
consumes: source: {from: mysql, deployment: ((primary_deployment_name)) }Plus or minus some bosh syntax cleanup here.
Maybe a second link isn't so bad, but it adds some more maintenance overhead where if we expose another mysql job property we have to maintain it in two places going forward.
There was a problem hiding this comment.
My reasoning was mostly about being specific what the link type is used for and not wanting to introduce additional properties to existing consumers to avoid confusion. I have no strong feelings about either direction.
| engine_config.enable_replication_target: | ||
| description: 'When configuring a replica, this will use spec.index to populate server_id on each node starting at the highest possible index. Any value set for server_id will be discarded by enabling this setting' | ||
| default: false | ||
| engine_config.enable_replication_source: | ||
| description: 'When configuring a replica, this will use spec.index to populate server_id on each node starting at the lowest possible index. Any value set for server_id will be discarded by enabling this setting' | ||
| default: false |
There was a problem hiding this comment.
I don't think these are used anymore and should be cleaned up.
| this is here for compatibility with older deployments of the pxc-release. | ||
| technically, the `roadmin` user is viable as a replication user, but it misses `REPLICATION SLAVE` privilege. | ||
| providing the source_admin_username & password will ensure that the roadmin will updated with the missing permission | ||
| monit_startup_timeout: |
There was a problem hiding this comment.
With bpm this is probably not super useful - the pid gets dropped as soon as the job starts and monit will treat the job as healthy.
But this made me wonder if we even really need monit for this use case. See my comments on the monit template.
| <%- end | ||
| allowed_remote_backup_hosts='localhost' | ||
|
|
||
| if p('engine_config.enable_replication_source') |
There was a problem hiding this comment.
Maybe it makes sense to just allow allowed_remote_backup_hosts to be configurable in some way. This seems similar to remote_admin_access and doesn't necessarily need to be tied to replication.
- add tls to mysql link: to allow for remote connection to use encryption, we need to somehow share the tls data. - the backup user currently does not accept connections that are not coming in through the local socket. Since the permissions on the user already contain everything that is required for replication to work, this adds the option to allow connections from remote hosts for that user.
this new job will be used to enable replication between two standalone deployments of the pxc-release
currently starts instances without ssl but with GTID enabled. should be useful to run local integration tests without deploying bosh instances..
else they won't be able to talk to each other.
initial implementation plus tests for connecting to a pxc instance to configure replication.
to succesfully start the replication, the replica depl should be starting from a backup of the source. mysqldump was used for it's simplicity and the fact that using the percona tools would probably require implementing an ssh connection between the replica and the source. This wasn't done to avoid adding additional creds to the job...
using the go-sql-driver didn't work for the restore. while it supports LOAD LOCAL FILE statements, it seems that the `source ...` command is not available. this adds the config of the mysql cli to run the restore via providing the dump via stdin to enable taking the required sync for the replication to properly start from a good state.
for replication to work each mysql server needs to use a unique id that is != `0`.
this wanted to interpolate a verb but used println. should use printf for that.
we can just shove them into a map so we can at least debug log them. without overloading the relevant fields of the struct.
never closing connections probably is bad, even though these are tests. not sure what the settings for open connections are in the pxc-containers. Avoid exhausting connections by closing them in tests. This was mostly added as a precaution to avoid connection issues ( that have been happening while intermittently while running the tests )
optionally print the container output to stdout by settings TEST_DEBUG. this is quite useful to see the mysql side of things while the container starts or while the mysql inside is used by the tests. improve container startup checks. some test executions seem to have run in timouts while cleaning the containers or failed with context deadline exceeded while waiting for the port to be available or the ready for connections log to be printed. this change alleviates those broken runs. set server-id for each test container: while adding tests for the e2e setup of the replication, the last step failed because the IO thread wasn't running. It wasn't running because the testcontainers didn't configure proper server-ids.
add e2e run of configuring replication on two fresh instances of pxc 8.4 to enable testing the basic workflow. fly-by: add wrapper to create source and target db connections quicker to autocomplete while working on the code than using the constants..
the initial tests pass and these shouldn't change much from now on, so time to commit the current state..
initial commit of current go related files.
- generate pass for replica user, it is capped at 20 char length - add struct for quicker test statements - change table structure for testsetups..
so we can sync the remote state to the local replica in one call fly-by: - remove setting the dumpfile name, using timestamps and the datadir should be good enough.
need to clarify because I have no idea about setting these..
so we can fail early if they're mismatched.
we also want to test the tls encryption enabled scenario
- if the replication is already enabled ( the query returns columns/values as opposed to an empty result set ), then we can probably skip the initial sync. This should help avoiding taking unnecessary dumps of the source. - add tls setup to testhelper to enable testing encrypted connections - enable creating tls connections, currently only verify_ca equivalent mode. requires registering a TLS config with the driver. We're exiting early after adding the ca, the rest will need to be added when client cert auth is considered. - enable client side param parsing. normal mysql param expansion only applies to SELECT/UPDATE/INSERT/DELETE. Apparently it is not used for administrative statements. The assumption is that the sql driver will do a better job at preventing injections than I could. While there's no current scenario for injections I can think of, maybe there's going to be one down the road. https://github.com/go-sql-driver/mysql#interpolateparams - add more params to connect calls, for now just picking the dbname.. maybe can be used to set additionall connection params down the road. it's only really used in GenerateTestData helper currently.. fly-by: - remove HostConfig struct, as it ended up being unused.. - extract CloseAndLogError, use Closer interface as input for generalization... - add replication-enabled tests
some new deps we're added for tests that will be commited later.
- check for value being nil before accessing it because else you'll have panics.. - fix constants. they were missmatched in name/value
- return early in stringer for state if we still see the default value of Enabled, then we can skip the rest.. - the dump related log moved to the dumper - remove some newlines from the sql statements.. - don't use pointer receivers. no need and at least this way we know there are no sideeffects by accident.
StartContainerInstance now also returns the actual testcontainer. we need this to copy the source-ca to the target container for integration/e2e tests. normally this file would be written by the pxc-replicator process from its config, but in the testsetup it runs in it's own isolated.
there was no main, now there is.. we need to call something and that will be it. add tests for compilation and some expectations on log output of the pxc replicator binary. the testsetup is `quite` close to what we'd have running on a bosh VM but avoids having to go through the whole bosh deploy / compile for quicker iteration. fly-by: switch cert struct to use string fields because that makes for more readable yaml.
- use context everywhere for sql calls, we don't want them to block forever. - simplify parsing code. log errors if we fail parsing single values instead of returning an error. don't think we need to fail on some values missing.
…are the source certs with the target if any are present
- Use BinPath everywhere - log after checking error in GetCommand.. - use constants for mysqlBin and dumpBin instead of hardcoding string - add checks for existence of binaries under BinPath
all config is now a single config.yml. all other files will be written by the replicator process after parsing the config.yml
remove the json import that was useful while debugging.. no need to dump the replState to stdout in tests anymore.
two different import paths have been used for yaml packages, that's not necessary.. use the go version that is rendered into the release, the compilation blew up with 1.25.3 being unavailable.
for compatibility with older pxc-releases, this allows configuring an admin_user on the target and a desired user/password combination for the replication user. The old releases provide remote admin access and we can utilize that to do the relevant configuration for the replication to be setup. this sets up a replication user that will be used for the setup IF both set of creds were specified. it will error out when the expectations are not met. This is done to avoid suggesting automatic user management. The user will be created and used as well as updated, but not deleted. This will enable interested parties to consume the replication before the version of the pxc release containing that feature made it into cf deployment.
by moving out some of the conditional logic from the yaml section..
when we find cached backups of the source, check the contained GTID. If the backup contains a GTID that is still `connectable` to the sources state at time of checking, skip taking a new backup and use the existing one. the flake attempts were added because the tests fail in the BeforeEach when run in parallel because the "docker host" is too slow to start the container within timeouts.. removed option to set name for StartContainerInstance to avoid duplicate names for source/target.. That would overwrite the contents of expected files if two tests named the instances the same way AND the tests relied on the default DataDir location (/tmp).. Just generating unique names avoids this.. fly-by: - centralize writing config files. previously config files were called on each call to args() in dumper and client. this seems unnecessary, since we can write the file on initialization of the client.. - separate data and dump dirs in dumper as well.
now only backups that are still viable are kept around. One is enough to be able to restart the sync without having to do a full dump on the source again. this is done in hopes that will it take off pressure from the source node if the replication unexpectedly breaks.
the target didn't properly yaml indent the certs. this fixes it and additionally optimizes the templating part for readability.
it has been working realiably
fly-by: - keep variable name consitent - continue on failure case for restorable checks. all failures should lead to skipping the file and retrying cleanup later. - unify around calling just log.Printf
1b3781e to
839a107
Compare
- add option to set the clean_expired_backups config, so the disk won't fill up with unusable backups - add EVENT grant to backup user, else events cannot be backed up. - switch link tls to tls.client, this should avoid exposing the server private key to external deployments.
after learning more about go-sql driver this seems like the right thing to do. the connections should be properly re-used and cached. this implements just that.
no point in trying to read a file we couldn't open
Hi there, I'd like to be able to do replica setups with the release. Here's my idea, let me know what you think of the approach.
Feature or Bug Description
it adds a jobs and a few config options to enable cross deployment replication for the pxc release.
Motivation
I want continuous backups of my running pxc cluster
Related Issue
If this PR was first opened as an issue, please provide the link to that issue here.