To make Seata highly available using a configuration centre and database, take Nacos and MySQL as an example and deploy the [cloud-seata-nacos](https://github.com/helloworlde/spring-cloud-alibaba-component/blob/ master/cloud-seata-nacos/) application to a Kubernetes cluster.
The application uses Nacos as the configuration and registration centre, and has three services: order-service, pay-service, and storage-service. The order-service provides an interface for placing an order, and when the balance and inventory are sufficient, the order succeeds and a transaction is submitted; when they are insufficient, an exception is thrown, the order fails, and the transaction is rolled back. Rollback transaction
Preparation
You need to prepare available registry, configuration centre Nacos and MySQL, usually, the registry, configuration centre and database are already available and do not need special configuration, in this practice, for simplicity, only deploy a stand-alone registry, configuration centre and database, assuming they are reliable
- Deploying Nacos
 
Deploy Nacos on a server with port 8848 open for seata-server registration at 192.168.199.2.
docker run --name nacos -p 8848:8848 -e MODE=standalone nacos/nacos-server
- Deploying MySQL
 
Deploy a MySQL database to hold transaction data at 192.168.199.2.
docker run --name mysql -p 30060:3306-e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.7.17
Deploying seata-server
- Create the tables needed for seata-server.
 
Refer to script/server/db for the exact SQL, here we are using MySQL's script and the database name is seata.
You also need to create the undo_log table, see script/client/at/db/.
- Modify the seata-server configuration
 
Add the following configuration to the Nacos Configuration Centre, as described in script/config-center
service.vgroupMapping.my_test_tx_group=default
store.mode=db
store.db.datasource=druid
store.db.dbType=mysql
store.db.driverClassName=com.mysql.jdbc.
store.db.url=jdbc:mysql://192.168.199.2:30060/seata?useUnicode=true
store.db.user=root
store.db.password=123456
Deploying seata-server to Kubernetes
- seata-server.yaml
 
You need to change the ConfigMap's Registry and Configuration Centre addresses to the appropriate addresses
apiVersion: v1
kind: Service
metadata: name: seata-ha-server.yaml
 name: seata-ha-server
 namespace: default
 labels: app.kubernetes.io/name: seata-ha-server
   app.kubernetes.io/name: seata-ha-server
spec.
 type: ClusterIP
 spec: type: ClusterIP
   - port: 8091
     protocol: TCP
     name: http
 selector: app.kubernetes.io/name: seata-ha-server
   app.kubernetes.io/name: seata-ha-server
---apiVersion: apps/v1
apiVersion: apps/v1
kind: StatefulSet
metadata.
 name: seata-ha-server
 namespace: default
 labels: app.kubernetes.io/name: seata-ha-server
   app.kubernetes.io/name: seata-ha-server
spec: serviceName: seata-ha-server
 serviceName: seata-ha-server
 replicas: 3
 selector: seata-ha-server
   matchLabels.
     app.kubernetes.io/name: seata-ha-server
 template: seata-ha-server
   metadata.
     labels: app.kubernetes.io/name: seata-ha-server
       app.kubernetes.io/name: seata-ha-server
   spec.
     containers: name: seata-ha-server
       - name: seata-ha-server
         image: docker.io/seataio/seata-server:latest
         imagePullPolicy: IfNotPresent
         env: name: SEATA_CONFIG
           - name: SEATA_CONFIG_NAME
             value: file:/root/seata-config/registry
         ports: name: http
           - name: http
             containerPort: 8091
             protocol: TCP
         volumeMounts: name: seata-config
           - name: seata-config
             mountPath: /root/seata-config
     volumes: name: seata-config mountPath: /root/seata-config
       - name: seata-config
         configMap: name: seata-ha-server-config
           name: seata-ha-server-config
---apiVersion: v1
apiVersion: v1
kind: ConfigMap
apiVersion: v1 kind: ConfigMap
 name: seata-ha-server-config
data: name: seata-ha-server-config
 registry.conf: |
   registry {
       type = "nacos"
       nacos {
         application = "seata-server"
         serverAddr = "192.168.199.2"
       }
   }
   config {
     type = "nacos"
     nacos {
       serverAddr = "192.168.199.2"
       group = "SEATA_GROUP"
     }
   }
- Deployment
 
kubectl apply -f seata-server.yaml
When the deployment is complete, there will be three pods
kubectl get pod | grep seata-ha-server
seata-ha-server-645844b8b6-9qh5j 1/1 Running 0 3m14s
seata-ha-server-645844b8b6-pzczs 1/1 Running 0 3m14s
seata-ha-server-645844b8b6-wkpw8 1/1 Running 0 3m14s
After the startup is complete, you can find three instances of seata-server in the Nacos service list, so you have completed the highly available deployment of seata-server.
- Viewing the service logs
 
kubelet logs -f seata-ha-server-645844b8b6-9qh5j
[0.012s][info ][gc] Using Serial
2020-04-15 00:55:09.880 INFO [main]io.seata.server.ParameterParser.init:90 -The server is running in container.
2020-04-15 00:55:10.013 INFO [main]io.seata.config.FileConfiguration.<init>:110 -The configuration file used is file:/root/seata- config/registry.conf
2020-04-15 00:55:12.426 INFO [main]com.alibaba.druid.pool.DruidDataSource.init:947 -{dataSource-1} inited
2020-04-15 00:55:13.127 INFO [main]io.seata.core.rpc.netty.RpcServerBootstrap.start:155 -Server started
where {dataSource-1}  indicates that the database is used and initialised properly
- Looking at the registry, there are three instances of the seata-serve service at this point
 
Deploying the business service
- Create business tables and initialise data
 
You can refer to [cloud-seata-nacos/README.md](https://github.com/helloworlde/spring-cloud-alibaba-component/blob/master/cloud-seata- nacos/README.md).
- Adding Nacos Configuration
 
Under the public namespace, create the configurations with data-id order-service.properties, pay-service.properties, storage-service.properties, with the same content. password
# MySQL
spring.datasource.url=jdbc:mysql://192.168.199.2:30060/seata?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true &useSSL=false
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.
# Seata
spring.cloud.alibaba.seata.tx-service-group=my_test_tx_group
- Deploying the Service
 
Deploy the service via the application.yaml configuration file, and note that you need to change the NACOS_ADDR of the ConfigMap to your Nacos address.
apiVersion: v1
kind: Service
metadata: namespace: default
 namespace: default
 name: seata-ha-service
 labels: app.kubernetes.io/name: seata-ha-service
   app.kubernetes.io/name: seata-ha-service
spec.
 type: NodePort
 spec: type: NodePort
   - nodePort: 30081
     nodePort: 30081
     protocol: TCP
     name: http
 selector: app.kubernetes.io/name: seata-ha-service
   app.kubernetes.io/name: seata-ha-service
---
apiVersion: v1
kind: ConfigMap
metadata: name: seata-ha-service-config
 name: seata-ha-service-config
data: NACOS_ADDR: 192.168.199.2:8848
 NACOS_ADDR: 192.168.199.2:8848
---apiVersion: v1
apiVersion: v1
kind: ServiceAccount
metadata: name: seata-ha-account
 name: seata-ha-account
 namespace: default
---apiVersion: rbac.authorisation.k8s.io/v1beta1
apiVersion: rbac.authorisation.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata: name: seata-ha-account
 name: seata-ha-account
roleRef.
 apiGroup: rbac.authorisation.k8s.io/v1beta1 kind: ClusterRoleBinding
 roleRef: apiGroup: rbac.authorisation.k8s.io
 name: cluster-admin
subjects.
 - kind: ServiceAccount
   name: seata-ha-account
   namespace: default
---
apiVersion: apps/v1
kind: Deployment
namespace: default --- --- apiVersion: apps/v1 kind: Deployment
 namespace: default
 name: seata-ha-service
 labels: app.kubernetes.io/name: seata-ha-service
   app.kubernetes.io/name: seata-ha-service
spec: replicas: 1
 replicas: 1
 selector.
   matchLabels: app.kubernetes.io/name: seata-ha-service
     app.kubernetes.io/name: seata-ha-service
 template: seata-ha-service
   metadata: seata-ha-service template.
     labels: app.kubernetes.io/name: seata-ha-service
       app.kubernetes.io/name: seata-ha-service
   spec: serviceAccountName: seata-ha-service
     serviceAccountName: seata-ha-account
     containers: name: seata-ha-order
       - name: seata-ha-order-service
         image: "registry.cn-qingdao.aliyuncs.com/hellowoodes/seata-ha-order-service:1.1"
         imagePullPolicy: IfNotPresent
         imagePullPolicy: IfNotPresent
           - name: NACOS_ADDR
             valueFrom.
               configMapKeyRef.
                 key: NACOS_ADDR
                 name: seata-ha-service-config
         name: seata-ha-service-config
           - name: http
             containerPort: 8081
             protocol: TCP
       - name: seata-ha-pay-service
         image: "registry.cn-qingdao.aliyuncs.com/hellowoodes/seata-ha-pay-service:1.1"
         imagePullPolicy: IfNotPresent
         env.
           - name: NACOS_ADDR
             valueFrom.
               configMapKeyRef.
                 key: NACOS_ADDR
                 name: seata-ha-service-config
         name: seata-ha-service-config
           - name: http
             containerPort: 8082
             protocol: TCP
       - name: seata-ha-storage-service
         image: "registry.cn-qingdao.aliyuncs.com/hellowoodes/seata-ha-storage-service:1.1"
         imagePullPolicy: IfNotPresent
         env.
           - name: NACOS_ADDR
             valueFrom.
               NACOS_ADDR valueFrom: NACOS_ADDR valueFrom: NACOS_ADDR valueFrom.
                 key: NACOS_ADDR
                 name: seata-ha-service-config
         name: seata-ha-service-config
           - name: http
             containerPort: 8083
             protocol: TCP
Deploy the application to the cluster with the following command
kubectl apply -f application.yaml
Then look at the pods that were created, there are three pods under the seata-ha-service service
kubectl get pod | grep seata-ha-service
seata-ha-service-7dbdc6894b-5r8q4 3/3 Running 0 12m
Once the application is up and running, in the Nacos service list, there will be the corresponding service
At this point, if you look at the service's logs, you will see that the service has registered with each of the TC's
kubectl logs -f seata-ha-service-7dbdc6894b-5r8q4 seata-ha-order-service
! seata-ha-service-register.png
Looking at any TC log, you'll see that each service is registered with the TC
kubelet logs -f seata-ha-server-645844b8b6-9qh5j
Test
Test Success Scenario
Call the order interface, set the price to 1, because the initial balance is 10, and the order is placed successfully.
curl -X POST \
http://192.168.199.2:30081/order/placeOrder \
-H 'Content-Type: application/json' \
-d '{
"userId": 1,
"productId": 1,
"price": 1
}'
At this point the return result is:
{"success":true, "message":null, "data":null}
Checking the TC logs, the transaction was successfully committed:
! seata-ha-commit-tc-success.png
View the order-service service log ! seata-ha-commit-success.png
Test failure scenario
If you set the price to 100 and the balance is not enough, the order fails and throws an exception, and the transaction is rolled back.
curl -X POST \
http://192.168.199.2:30081/order/placeOrder \
-H 'Content-Type: application/json' \
-d '{
"userId": 1,
"productId": 1,
"price": 100
}'
View the logs for TC: ! seata-ha-commit-tc-rollback.png
View the logs of the service : ! seata-ha-commit-service-rollback.png
Multiple calls to view the service logs reveal that transaction registration is randomly initiated to one of the T Cs, and when capacity is expanded or scaled down, the corresponding TC participates or withdraws, proving that the high availability deployment is in effect.