How to Use Shared Disk PersistentVolumes for Oracle Kubernetes Engine (OKE) in Oracle Cloud Infrastructure (OCI)

上一篇文章我们尝试了在Oracle Cloud的容器云里创建了持久化卷,并且使用该卷创建了一个MySQL数据库的容器。这样数据库里的数据就保存在了容器外部,如果容器崩溃,那么Kubernetes会根据Deployment的定义,重新创建一个容器,而数据则不会丢失。

对于企业在使用存储来说,除了上述的数据持久性保存之外,共享性也是通常会考虑的一个因素,也就是一份存储上的数据同时被多个应用共享读写。

本文阐述如何在Oracle Cloud的容器云中创建和使用共享存储,在这篇文章中我们使用NFS的方式来实现共享。

创建文件系统

首先需要在Oracle Cloud的管理后台创建一个文件系统,菜单项位于File Storage->File Systems。
NewImage

点击页面中的“Create File System”按钮,在新建文件系统的页面中点击“Edit Details”,可以对名字进行修改,实际上直接全部使用默认值也是可以的。
NewImage

为了表意更清晰,我们将文件系统的名字修改为FileSystem-for-OKE,将Export路径修改为“/oke-export”,Mount Target名称不做变化。
这些Oracle Cloud中文件系统的术语的解释,可以参看官方文档。注意的是:其中Export路径在后续的容器配置中会使用到,不过这个路径的名称跟容器中会挂载到的路径没有任何关系,可以随意命名。

修改后的并且创建成功的文件系统,可以在管理后台中看到详细情况。
NewImage

同样,通过oci命令行也可以获得创建成功的文件系统的信息。

$ oci fs file-system list \
> --compartment-id ocid1.tenancy.oc1..aaaaaaaaqxee2wwzpx3s6usguambydweb2q5yfx2y5ux3lwlrpaao4gdun7a \
> --availability-domain lOnA:AP-TOKYO-1-AD-1
{
  "data": [
    {
      "availability-domain": "lOnA:AP-TOKYO-1-AD-1",
      "compartment-id": "ocid1.tenancy.oc1..aaaaaaaaqxee2wwzpx3s6usguambydweb2q5yfx2y5ux3lwlrpaao4gdun7a",
      "defined-tags": {},
      "display-name": "FileSystem-for-OKE",
      "freeform-tags": {},
      "id": "ocid1.filesystem.oc1.ap_tokyo_1.aaaaaaaaaaaafictnzzhillqojxwiotboawxi33lpfxs2mjnmfsc2mia",
      "kms-key-id": "",
      "lifecycle-state": "ACTIVE",
      "metered-bytes": 8704,
      "time-created": "2019-10-21T13:26:40+00:00"
    }
  ]
}

在创建文件系统时,Oracle Cloud会自动创建一个Mount Target,可以在管理后台中看到详细情况。
NewImage

注意这里的IP地址,这个地址在后续创建容器持久化卷的时候也会使用到。

同样,通过oci命令行也可以获得创建成功的挂载目标的信息。

$ oci fs mount-target list \
--compartment-id ocid1.tenancy.oc1..aaaaaaaaqxee2wwzpx3s6usguambydweb2q5yfx2y5ux3lwlrpaao4gdun7a \
--availability-domain lOnA:AP-TOKYO-1-AD-1
{
  "data": [
    {
      "availability-domain": "lOnA:AP-TOKYO-1-AD-1",
      "compartment-id": "ocid1.tenancy.oc1..aaaaaaaaqxee2wwzpx3s6usguambydweb2q5yfx2y5ux3lwlrpaao4gdun7a",
      "defined-tags": {},
      "display-name": "MountTarget-20191021-1322",
      "export-set-id": "ocid1.exportset.oc1.ap_tokyo_1.aaaaaa4np2snfy33nzzhillqojxwiotboawxi33lpfxs2mjnmfsc2mia",
      "freeform-tags": {},
      "id": "ocid1.mounttarget.oc1.ap_tokyo_1.aaaaaa4np2snfy34nzzhillqojxwiotboawxi33lpfxs2mjnmfsc2mia",
      "lifecycle-state": "ACTIVE",
      "private-ip-ids": [
        "ocid1.privateip.oc1.ap-tokyo-1.aaaaaaaa34v43wf2b3j73kme2rtho4rlpm6kit3avszgirkpqm5uspbqnrrq"
      ],
      "subnet-id": "ocid1.subnet.oc1.ap-tokyo-1.aaaaaaaafqsuofe2e34mnfkotolakrzktxmwdasj7dyov3u3jixqablc2qgq",
      "time-created": "2019-10-21T13:26:42+00:00"
    }
  ]
}

可以参看创建文件系统的官方文档

创建PV

创建一个PV的定义yaml文件,内容如下。server部分指定上述的Mount Target的IP地址,path部分指定上述的File System的名字。

$ cat pv-nfs.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfs
spec:
  capacity:
    storage: 1Gi
  accessModes:
    - ReadWriteMany
  nfs:
    server: 10.0.10.5
    path: "/oke-export"

创建PV,并检查创建结果。

$ kubectl create -f pv-nfs.yaml

$ kubectl get pv
NAME   CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE
nfs    1Gi        RWX            Retain           Available                                   8s

$ kubectl describe pv nfs
Name:            nfs
Labels:          <none>
Annotations:     pv.kubernetes.io/bound-by-controller: yes
Finalizers:      [kubernetes.io/pv-protection]
StorageClass:
Status:          Available
Claim:           default/nfs
Reclaim Policy:  Retain
Access Modes:    RWX
VolumeMode:      Filesystem
Capacity:        1Gi
Node Affinity:   <none>
Message:
Source:
    Type:      NFS (an NFS mount that lasts the lifetime of a pod)
    Server:    10.0.10.5
    Path:      /oke-export
    ReadOnly:  false
Events:        <none>

可以看到现在PV的状态是Available,表示可用,在后续我们创建完PVC之后,这里会显示成Bound,表示该PV已经跟PVC绑定。

创建PVC

创建一个PVC的定义yaml文件,内容如下。

$ cat pvc-nfs.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: nfs
spec:
  accessModes:
    - ReadWriteMany
  storageClassName: ""
  resources:
    requests:
      storage: 1Mi

创建PVC,并检查创建结果。

$ kubectl create -f pvc-nfs.yaml

$ kubectl get pvc
NAME   STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
nfs    Bound    nfs      1Gi        RWX                           4s

$ kubectl describe pvc nfs
Name:          nfs
Namespace:     default
StorageClass:
Status:        Bound
Volume:        nfs
Labels:        <none>
Annotations:   pv.kubernetes.io/bind-completed: yes
               pv.kubernetes.io/bound-by-controller: yes
Finalizers:    [kubernetes.io/pvc-protection]
Capacity:      1Gi
Access Modes:  RWX
VolumeMode:    Filesystem
Events:        <none>
Mounted By:    <none>

创建容器

这里我们使用ReplicationController类型的对象来创建Pod,这样可以指定自动创建多个容器,并共享读写指定的NFS存储。
创建一个RC的定义yaml文件,内容如下。作为测试,我们使用最简单的Nginx镜像,并且指定2个复制。

$ cat rc-nfs.yaml
apiVersion: v1
kind: ReplicationController
metadata:
  name: rc-nfs-test
spec:
  replicas: 2
  selector:
    app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
          - name: nginx
            containerPort: 80
        volumeMounts:
            - name: nfs
              mountPath: "/usr/share/nginx/html"
      volumes:
      - name: nfs
        persistentVolumeClaim:
          claimName: nfs

创建RC,并检查结果。

$ kubectl create -f rc-nfs.yaml

$ kubectl get rc rc-nfs-test
NAME          DESIRED   CURRENT   READY   AGE
rc-nfs-test   2         2         2       6m56s

$ kubectl describe replicationcontrollers/rc-nfs-test
Name:         rc-nfs-test
Namespace:    default
Selector:     app=nginx
Labels:       app=nginx
Annotations:  <none>
Replicas:     2 current / 2 desired
Pods Status:  2 Running / 0 Waiting / 0 Succeeded / 0 Failed
Pod Template:
  Labels:  app=nginx
  Containers:
   nginx:
    Image:        nginx
    Port:         80/TCP
    Host Port:    0/TCP
    Environment:  <none>
    Mounts:
      /usr/share/nginx/html from nfs (rw)
  Volumes:
   nfs:
    Type:       PersistentVolumeClaim (a reference to a PersistentVolumeClaim in the same namespace)
    ClaimName:  nfs
    ReadOnly:   false
Events:
  Type    Reason            Age   From                    Message
  ----    ------            ----  ----                    -------
  Normal  SuccessfulCreate  16m   replication-controller  Created pod: rc-nfs-test-bqhm9
  Normal  SuccessfulCreate  16m   replication-controller  Created pod: rc-nfs-test-8h884

$ kubectl get pods
NAME                                READY   STATUS    RESTARTS   AGE
rc-nfs-test-8h884                   1/1     Running   0          3m16s
rc-nfs-test-bqhm9                   1/1     Running   0          3m16s

到目前为止,我们已经创建好了一组(2个)共享使用磁盘的Nginx容器,共享磁盘在容器内的挂载路径是:/usr/share/nginx/html

接下来,测试一下共享读写的效果。计划在一个容器的共享磁盘中创建index.html文件,并且写入了”test sharing disk”这样的文本,之后从另外一个容器中进行查看。

首先,在其中一个容器里创建index.html文件。

$ kubectl exec rc-nfs-test-8h884 touch /usr/share/nginx/html/index.html

写入一行文本。

$ kubectl exec -it rc-nfs-test-8h884 -- /bin/bash
root@rc-nfs-test-8h884:/# cd /usr/share/nginx/html/
root@rc-nfs-test-8h884:/usr/share/nginx/html# ls
index.html
root@rc-nfs-test-8h884:/usr/share/nginx/html# echo "test sharing disk">>/usr/share/nginx/html/index.html

现在我们登录到另外一个容器中,查看是否能够读取到该内容。

$ kubectl exec rc-nfs-test-bqhm9 cat /usr/share/nginx/html/index.html
test sharing disk

Well Done!

How to Deploy Container MySQL Database with Persistent Volumes in Oracle Cloud

上一篇文章,提到了如何在Oracle Cloud中创建和访问Kubernetes Cluster

接下来,这篇文章我们在这个Kubernetes Cluster中创建一个MySQL数据库,并且要使用到容器外的持久化卷来存储数据。

概念介绍

在Kubernetes的存储使用中,至少有两个概念需要了解,一个是Persistent Volumes(PV),另外一个是PersistentVolumeClaims(PVC)。我们可以这样理解,PV就像是真正的资源,就像CPU、内存一样的,只是PV是存储资源;而PVC则是任何一个容器要使用这些资源必须指定的纽带,对于容器来说,只看得到PVC。PVC在定义阶段并不会指明绑定到哪一个PV上,但是在创建之后则会由Kubernetes Controller来在有效的PV中寻找合适的绑定对象,一旦绑定,PV和PVC就形成了一对一的关系。从而,容器->PVC-PV,这样就完成了在容器中使用存储的目的。

前面说,PVC在创建之后会由Controller在有效的PV中寻找合适的绑定对象,实际上也可以在需要的时候自动创建PV然后自动绑定,这被称为Dynamic Volume Provisioning,要实现这一点,需要在Kubernetes中先定义StorageClass。StorageClass中定义了存储提供者(provisioner)还有重分配策略(reclaimPolicy)等,而provisioner实际上就是各种Kubernetes支持的存储类型,分别由各个存储厂家自己制定的,以volume plugin的方式加入到Kubernetes中,比如三大云厂商都提供了自己的存储,微软提供了Azure File和Azure Disk,AWS提供了AWS EBS,Google提供了GCE PD,另外还有其它各种存储类型,比如Ceph,Openstack Cinder,Glusterfs,ScaleIO,vSphere都有相应的provisioner。

同样Oracle Cloud也提供了自己的provisioner,并且在OKE中已经预先创建好了默认的StorageClass。

$ kubectl get StorageClass
NAME            PROVISIONER      AGE
oci (default)   oracle.com/oci   4d6h

$ kubectl describe StorageClass/oci
Name:            oci
IsDefaultClass:  Yes
Annotations:     kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"storage.k8s.io/v1beta1","kind":"StorageClass","metadata":{"annotations":{"storageclass.beta.kubernetes.io/is-default-class":"true"},"name":"oci","namespace":""},"provisioner":"oracle.com/oci"}
,storageclass.beta.kubernetes.io/is-default-class=true
Provisioner:           oracle.com/oci
Parameters:            <none>
AllowVolumeExpansion:  <unset>
MountOptions:          <none>
ReclaimPolicy:         Delete
VolumeBindingMode:     Immediate
Events:                <none>

因此,只需要在创建PVC的时候,指定StorageClass=oci,就可以实现Dynamic Volume Provisioning。

定义配置文件

配置文件的命名可以随意,这里我们起名为:pvc-dynamic.yaml

$ cat pvc-dynamic.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc1
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: "oci"
  resources:
    requests:
      storage: 50Mi

创建PVC(同时自动创建PV)

$ kubectl create -f pvc-dynamic.yaml
persistentvolumeclaim/pvc1 created

可以看到pvc1在创建以后,已经处于Bound状态,也就是已经跟PV绑定了,而VOLUME列就是自动创建的PV

$ kubectl get pvc pvc1
NAME   STATUS   VOLUME                                                                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
pvc1   Bound    ocid1.volume.oc1.ap-tokyo-1.abxhiljro7qdtft2mo7jukuhk7hvtpvtwbb6z5zrpkz55zq4lxozorfn7pwq   50Gi       RWO            oci            73s

查看这个自动创建的PV,可以看到即使我们在配置文件中只要求了50MB,创建的PV容量却是50GB,这是Oracle Cloud中最小的PV尺寸,50GB起步。

$ kubectl describe persistentvolume/ocid1.volume.oc1.ap-tokyo-1.abxhiljro7qdtft2mo7jukuhk7hvtpvtwbb6z5zrpkz55zq4lxozorfn7pwq
Name:            ocid1.volume.oc1.ap-tokyo-1.abxhiljro7qdtft2mo7jukuhk7hvtpvtwbb6z5zrpkz55zq4lxozorfn7pwq
Labels:          failure-domain.beta.kubernetes.io/region=ap-tokyo-1
                 failure-domain.beta.kubernetes.io/zone=AP-TOKYO-1-AD-1
Annotations:     ociAvailabilityDomain: AP-TOKYO-1-AD-1
                 ociCompartment: ocid1.tenancy.oc1..aaaaaaaaqxee2wwzpx3s6usguambydweb2q5yfx2y5ux3lwlrpaao4gdun7a
                 ociProvisionerIdentity: ociProvisionerIdentity
                 ociVolumeID: ocid1.volume.oc1.ap-tokyo-1.abxhiljro7qdtft2mo7jukuhk7hvtpvtwbb6z5zrpkz55zq4lxozorfn7pwq
                 pv.kubernetes.io/provisioned-by: oracle.com/oci
Finalizers:      [kubernetes.io/pv-protection]
StorageClass:    oci
Status:          Bound
Claim:           default/pvc1
Reclaim Policy:  Delete
Access Modes:    RWO
VolumeMode:      Filesystem
Capacity:        50Gi
Node Affinity:   <none>
Message:
Source:
    Type:       FlexVolume (a generic volume resource that is provisioned/attached using an exec based plugin)
    Driver:     oracle/oci
    FSType:     ext4
    SecretRef:  nil
    ReadOnly:   false
    Options:    map[]
Events:         <none>

在Oracle Cloud中,自动创建的PV是以Block Storage服务中的Block Volumes方式来提供的。
Oci pvc1

在管理界面中可以看到Block Volumes的名字就是指定创建的PVC的名字:pvc1。同时还能注意到,这个卷已经Attach到一个虚拟机实例中,实际上这个实例就是Kubernetes集群中的一个Work Node。这些全部都是在一条kubectl create命令之后,Oracle Cloud自动完成的工作。

我们还可以通过oci命令行检查存储卷的情况。如何配置OCI命令行我们也在上一篇文章中提过。

$ oci bv volume get --volume-id ocid1.volume.oc1.ap-tokyo-1.abxhiljro7qdtft2mo7jukuhk7hvtpvtwbb6z5zrpkz55zq4lxozorfn7pwq
{
  "data": {
    "availability-domain": "lOnA:AP-TOKYO-1-AD-1",
    "compartment-id": "ocid1.tenancy.oc1..aaaaaaaaqxee2wwzpx3s6usguambydweb2q5yfx2y5ux3lwlrpaao4gdun7a",
    "defined-tags": {},
    "display-name": "pvc1",
    "freeform-tags": {},
    "id": "ocid1.volume.oc1.ap-tokyo-1.abxhiljro7qdtft2mo7jukuhk7hvtpvtwbb6z5zrpkz55zq4lxozorfn7pwq",
    "is-hydrated": true,
    "kms-key-id": null,
    "lifecycle-state": "AVAILABLE",
    "size-in-gbs": 50,
    "size-in-mbs": 51200,
    "source-details": null,
    "system-tags": {},
    "time-created": "2019-10-21T16:09:10.178000+00:00",
    "volume-group-id": null
  },
  "etag": "432d18081fd598a1ddacddfd8982f8cb"
}

创建MySQL数据库的服务和pod

接下来就是常见的创建MySQL容器的方法了。我们在volumes中指定了pvc1(claimName: pvc1),并且将pvc1绑定的PV挂载到容器中的/var/lib/mysql目录下(mountPath: /var/lib/mysql),从而实现整个MySQL的数据文件都保存在了容器之外的持久化存储卷中。

$ cat mysql-deployment.yaml
apiVersion: v1
kind: Service
metadata:
  name: mysql-service
  labels:
    app: mysql
spec:
    selector:
      app: mysql
    ports:
      - port: 3306
    clusterIP: None
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysql
spec:
  selector:
    matchLabels:
      app: mysql
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
      - image: mysql:8.0
        name: mysql
        env:
          # Use secret in real usage
        - name: MYSQL_ROOT_PASSWORD
          value: welcome1
        ports:
        - containerPort: 3306
          name: mysql
        volumeMounts:
        - name: mysql-pv-storage
          mountPath: /var/lib/mysql
      volumes:
      - name: mysql-pv-storage
        persistentVolumeClaim:
          claimName: pvc1

$ kubectl apply -f mysql-deployment.yaml
service/mysql-service created
deployment.apps/mysql created

$ kubectl get pods --selector=app=mysql
NAME                     READY   STATUS    RESTARTS   AGE
mysql-749cb8f84b-d5qxm   1/1     Running   0          97s

登入到容器中,检查结果显示:挂载正常,数据文件创建正常。

$ kubectl exec -it mysql-749cb8f84b-d5qxm -- /bin/bash
root@mysql-749cb8f84b-d5qxm:/# df -h
Filesystem      Size  Used Avail Use% Mounted on
overlay          39G  4.1G   35G  11% /
tmpfs            64M     0   64M   0% /dev
tmpfs           5.8G     0  5.8G   0% /sys/fs/cgroup
/dev/sda3        39G  4.1G   35G  11% /etc/hosts
shm              64M     0   64M   0% /dev/shm
/dev/sdb         50G  228M   47G   1% /var/lib/mysql
tmpfs           5.8G   12K  5.8G   1% /run/secrets/kubernetes.io/serviceaccount
tmpfs           5.8G     0  5.8G   0% /proc/acpi
tmpfs           5.8G     0  5.8G   0% /proc/scsi
tmpfs           5.8G     0  5.8G   0% /sys/firmware

root@mysql-749cb8f84b-d5qxm:/# cd /var/lib/mysql
root@mysql-749cb8f84b-d5qxm:/var/lib/mysql# ls -l
total 178208
drwxr-x---. 2 mysql mysql     4096 Oct 21 16:41 #innodb_temp
-rw-r-----. 1 mysql mysql       56 Oct 21 16:41 auto.cnf
-rw-r-----. 1 mysql mysql  3084516 Oct 21 16:41 binlog.000001
-rw-r-----. 1 mysql mysql      155 Oct 21 16:41 binlog.000002
-rw-r-----. 1 mysql mysql       32 Oct 21 16:41 binlog.index
-rw-------. 1 mysql mysql     1676 Oct 21 16:41 ca-key.pem
-rw-r--r--. 1 mysql mysql     1112 Oct 21 16:41 ca.pem
-rw-r--r--. 1 mysql mysql     1112 Oct 21 16:41 client-cert.pem
-rw-------. 1 mysql mysql     1676 Oct 21 16:41 client-key.pem
-rw-r-----. 1 mysql mysql     5416 Oct 21 16:41 ib_buffer_pool
-rw-r-----. 1 mysql mysql 50331648 Oct 21 16:41 ib_logfile0
-rw-r-----. 1 mysql mysql 50331648 Oct 21 16:41 ib_logfile1
-rw-r-----. 1 mysql mysql 12582912 Oct 21 16:41 ibdata1
-rw-r-----. 1 mysql mysql 12582912 Oct 21 16:41 ibtmp1
drwx------. 2 mysql root     16384 Oct 21 16:41 lost+found
drwxr-x---. 2 mysql mysql     4096 Oct 21 16:41 mysql
-rw-r-----. 1 mysql mysql 30408704 Oct 21 16:41 mysql.ibd
drwxr-x---. 2 mysql mysql     4096 Oct 21 16:41 performance_schema
-rw-------. 1 mysql mysql     1680 Oct 21 16:41 private_key.pem
-rw-r--r--. 1 mysql mysql      452 Oct 21 16:41 public_key.pem
-rw-r--r--. 1 mysql mysql     1112 Oct 21 16:41 server-cert.pem
-rw-------. 1 mysql mysql     1676 Oct 21 16:41 server-key.pem
drwxr-x---. 2 mysql mysql     4096 Oct 21 16:41 sys
-rw-r-----. 1 mysql mysql 12582912 Oct 21 16:41 undo_001
-rw-r-----. 1 mysql mysql 10485760 Oct 21 16:41 undo_002

下一篇文章计划介绍,如何在Oracle Cloud的容器云中创建和使用共享磁盘。

MySQL Enterprise Audit in Oracle MySQL Cloud Service

Oracle Cloud Service似乎是唯一个即使你只提交了创建RDS服务的请求,也会自动帮你创建一个虚拟主机,并且提供主机登录权限的服务提供商,也许这就是Oracle一直在宣称的IaaS+PaaS的整合。

在创建MySQL Cloud Service时,会要求提供一个SSH登录密钥,所有的Oracle云服务主机登录用户都是opc,因此在服务创建完毕,收到通知邮件之后,就可以通过以下命令登录到云主机上。

ssh -i id_rsa_oracle_cloud_enmotech opc@your_host_ip

登录到主机以后,屏幕欢迎词是个小惊喜,Oracle贴心得显示了MySQL运行状态和存储使用量的提示。
MySQL Cloud Service Host

在比较了多方的RDS之后,Oracle MySQL Cloud Service确实如一直以来宣传的那样,提供了最多的安全选件功能,如果我们用MySQL Workbench登录到数据库中,在Server Status页面可以看到:SSL连接,PAM验证,密码校验,数据库审计等多种安全功能全部是开启的。

Oracle RDS security

而与之相比,阿里云MySQL RDS提供的安全功能就少的可怜了。说少不合适,是一项安全功能都没开启。
Aliyun RDS security

Oracle对于数据库安全性的看重确实超越了大多数数据库提供商,而这也带来了一些小麻烦。

在创建MySQL云服务的时候,需要指定数据库root用户的密码,这个密码有比较强的安全要求,需要有大写、小写英文字母,有数字,同时还需要有特殊字符(比如#)。因此在后续创建Login Path时,在mysql_config_editor命令提示输入密码的时候,需要在密码前后加上双引号(比如”Passw#rd”),否则会一直出现拒绝访问的提示。

--opc用户没有办法直接登录MySQL,需要切换到oracle用户
[opc@mysql-cloud-mysql-1 ~]$ mysql -uroot -p
Please switch to "oracle" user to use mysql client
[opc@mysql-cloud-mysql-1 ~]$ sudo su - oracle
--使用密码直接登录是没有问题的
[oracle@mysql-cloud-mysql-1 ~]$ mysql -uroot -p
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 1509246
Server version: 5.7.17-enterprise-commercial-advanced-log MySQL Enterprise Server - Advanced Edition (Commercial)

Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> exit
Bye
--创建Login Path,仍然输入上述一样的密码
[oracle@mysql-cloud-mysql-1 ~]$ mysql_config_editor set --host=localhost --user=root --password
Enter password:
--检查确认Login Path已经创建成功
[oracle@mysql-cloud-mysql-1 ~]$ mysql_config_editor print --login-path=client
[client]
user = root
password = *****
host = localhost
--直接登录报错
[oracle@mysql-cloud-mysql-1 ~]$ mysql
ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: YES)
--在密码前后加上双引号重新创建Login Path
[oracle@mysql-cloud-mysql-1 ~]$ mysql_config_editor set --host=localhost --user=root --password
Enter password:
WARNING : 'client' path already exists and will be overwritten.
 Continue? (Press y|Y for Yes, any other key for No) : Y
--再次登录,成功
[oracle@mysql-cloud-mysql-1 ~]$ mysql
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 1510011
Server version: 5.7.17-enterprise-commercial-advanced-log MySQL Enterprise Server - Advanced Edition (Commercial)

Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> status
--------------
mysql  Ver 14.14 Distrib 5.7.17, for linux-glibc2.5 (x86_64) using  EditLine wrapper

Connection id:		1510011
Current database:
Current user:		root@localhost
SSL:			Not in use
Current pager:		stdout
Using outfile:		''
Using delimiter:	;
Server version:		5.7.17-enterprise-commercial-advanced-log MySQL Enterprise Server - Advanced Edition (Commercial)
Protocol version:	10
Connection:		Localhost via UNIX socket
Server characterset:	utf8mb4
Db     characterset:	utf8mb4
Client characterset:	utf8
Conn.  characterset:	utf8
UNIX socket:		/tmp/mysql.sock
Uptime:			10 days 13 hours 24 min 56 sec

Threads: 13  Questions: 50733146  Slow queries: 0  Opens: 6203  Flush tables: 1  Open tables: 4808  Queries per second avg: 55.610
--------------

在进行Audit功能的检查之前,对于默认的mysql命令行提示只有mysql> 这样简陋的显示不能忍,要加上当前登录的用户和数据库名称。在oracle用户的.my.cnf下增加以下行。

[mysql]
prompt=\\u@\\h [\\d]>\\_

再次登录,mysql命令行的提示就比较顺眼了。

[oracle@mysql-cloud-mysql-1 ~]$ mysql
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 1510385
Server version: 5.7.17-enterprise-commercial-advanced-log MySQL Enterprise Server - Advanced Edition (Commercial)

Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

root@localhost [(none)]> show databases;
+-------------------------+
| Database                |
+-------------------------+
| information_schema      |
| mem                     |
| mem__advisor_text       |
| mem__advisors           |
| mem__bean_config        |
| mem__config             |
| mem__enterprise         |
| mem__events             |
| mem__instruments        |
| mem__instruments_config |
| mem__inventory          |
| mem__quan               |
| mysql                   |
| performance_schema      |
| sys                     |
| testdb                  |
+-------------------------+
16 rows in set (0.00 sec)

root@localhost [(none)]> use testdb
Database changed
root@localhost [testdb]> show tables;
Empty set (0.00 sec)

root@localhost [testdb]>

接下来进入本文的正题,在Oracle MySQL Cloud Service中默认是如何设置Audit的?
首先先检查一下audit_log的相关参数设置。

root@localhost [testdb]> show variables like 'audit_log_%';
+-----------------------------+--------------+
| Variable_name               | Value        |
+-----------------------------+--------------+
| audit_log_buffer_size       | 1048576      |
| audit_log_connection_policy | ALL          |
| audit_log_current_session   | ON           |
| audit_log_exclude_accounts  |              |
| audit_log_file              | audit.log    |
| audit_log_filter_id         | 0            |
| audit_log_flush             | OFF          |
| audit_log_format            | NEW          |
| audit_log_include_accounts  |              |
| audit_log_policy            | ALL          |
| audit_log_rotate_on_size    | 1073741824   |
| audit_log_statement_policy  | ERRORS       |
| audit_log_strategy          | ASYNCHRONOUS |
+-----------------------------+--------------+
13 rows in set (0.01 sec)

那么audit_log_file=audit.log表示仍然使用了默认的名字,到MySQL的数据文件目录中检查一下audit.log文件的存在。

[oracle@mysql-cloud-mysql-1 mysql]$ ls -l audit*
-rw-r----- 1 oracle oracle  838348830 Apr 21 13:42 audit.log
-rw-r----- 1 oracle oracle 1073742130 Apr 14 23:20 audit.log.14922120508218349.xml

这里可以看到总共的audit日志已经有1.9GB之大,在第一个audit.log达到audit_log_rotate_on_size参数设置的大小之后,自动切换成了新的audit.log。

题外话,之所以有这么巨大的audit.log,是由于启用了MySQL Enterprise Monitor,不断地记录了Monitor进程的登录和退出,为了方便后面观察audit.log,先将Monitor停止。

[oracle@mysql-cloud-mysql-1 monitor]$ pwd
/u01/bin/enterprise/monitor
[oracle@mysql-cloud-mysql-1 monitor]$ ./mysqlmonitorctl.sh stop
Stopping tomcat service . [ OK ]

接下来进行一些常规操作,并观察audit.log文件的输出内容。

1. 用root用户登录,在audit文件中显示一条Connect类型的记录。TIMESTAMP记录了时间,USER标签记录了登录的用户,HOST标签记录了登录的机器,COMMAND_CLASS为connect。

NewImage

2. 尝试直接CTAS一张测试表,在enforce_gtid_consistency=ON时会报1786错误,这是GTID特性决定的。

root@localhost [testdb]> create table mytables as select * from information_schema.tables;
ERROR 1786 (HY000): Statement violates GTID consistency: CREATE TABLE ... SELECT.

在audit.log中也记录下了这次报错的操作,STATUS标签为错误代码1786,STATUS_CODE标签值为1表示这条SQL没有成功执行,SQLTEXT标签记录了整个SQL语句。

NewImage

3. 下面的三条语句,select,create table,insert均没有在audit.log中记录下来。

root@localhost [testdb]> select count(*) from information_schema.tables;
+----------+
| count(*) |
+----------+
|      591 |
+----------+
1 row in set (0.01 sec)

root@localhost [testdb]> create table mytables like information_schema.tables;
Query OK, 0 rows affected (0.10 sec)

root@localhost [testdb]> insert into mytables select * from information_schema.tables;
Query OK, 592 rows affected (1.94 sec)
Records: 592  Duplicates: 0  Warnings: 0

这样的行为是由audit_log_statement_policy=ERRORS参数决定的,默认只记录报错的SQL,而不会记录所有的执行语句。

4. 将audit_log_statement_policy参数修改为ALL,再执行同样的select语句。

root@localhost [testdb]> SET GLOBAL audit_log_statement_policy = ALL;
Query OK, 0 rows affected (0.00 sec)

root@localhost [testdb]> select count(*) from mytables;
+----------+
| count(*) |
+----------+
|      592 |
+----------+
1 row in set (0.00 sec)

可以发现audit.log中已经有记录了。

NewImage

结论:在Oracle MySQL Cloud Service中默认会启动MySQL Enterprise Audit组件,并且设置即记录用户登录又记录执行语句(audit_log_policy=ALL),用户登录情况则不管登录成功还是失败每次都记录(audit_log_connection_policy=ALL),而执行语句则只记录执行失败的语句(audit_log_statement_policy=ERRORS)。

Network Settings Configuration in Oracle Cloud

对于Oracle DBA而言,网络上的配置可能都是短板,而如果在公有云中进行Oracle数据库的部署,那么几乎要求一个DBA变成全栈工程师,因为已经不需要你进行网络基础架构的安装,那么对整个环境进行简单的网络设定配置就成为必不可少的技能之一。

本文会着重介绍Oracle Cloud中关于网络设定的概念和设置方法。

在这之前要先简单介绍Oracle Cloud。

Screen Shot 2016-04-09 at 2.56.16 PM

也许你已经听说过Amazon AWS、Microsoft Azure,甚至你已经在使用阿里云、华为云、腾讯云、青云,而Oracle无疑是这场云大战中的迟到者,但是从过去的2015年看,Oracle在Cloud市场上的表现却是不俗的,Oracle一直宣称自己是全球唯一最全面、最集成的一朵完整的云,提供了IaaS,PaaS,SaaS三个层面的所有产品,而让Oracle Cloud特别引人注意的,无疑是在RDBMS全球市场中占据霸主地位的Oracle数据库的云化。

Oracle Database在Oracle Cloud中目前有以下几种产品方式。

1. Database as a Service 提供一个客户独占的虚拟机,可以选择在其上安装的是11g还是12c数据库,包括可以选择安装一个2节点的RAC;客户对于虚拟机和运行在其上的数据库有完全的管理权限,包括操作系统的root权限,还有数据库的SYSDBA权限,提供一键备份和一键打补丁等简便的管理功能。

2. Exadata Service 提供一个运行在Exadata上的11.2.0.4或者12.1.0.2的数据库,客户可以选择是全配、半配还是四分之一配,还可以选择是否是RAC,同样有操作系统的root权限和数据库的SYSDBA权限。

3. Database Schema Service 提供一个在11g数据库中的Schema,客户可以选择存储空间大小(5GB-50GB),只能通过RESTful网络服务接口来访问该Schema中的表数据,毫无疑问,没有数据库的SYSDBA权限,操作系统也是完全不可见的。

4. Database as a Service – Managed(该服务产品目前还没有正式发布) 提供一个Oracle数据库给客户,客户拥有SYSDBA权限,可以以包括SQL*Net在内的各种方式访问数据库,操作系统不可见,Oracle原厂承担必要的维护工作,比如备份和PITR恢复、打补丁和升级。

本文中使用的产品是Database as a Service,一套Oracle 12.1.0.2 RAC。CDB架构,初始化创建时候设定了一个PDB。

Screen Shot 2016-04-05 at 4.07.20 PM

在数据库创建完毕以后,Oracle也默认创建了承载Oracle数据库的虚拟机,也就是底层的IaaS架构,在本文中是两台2CPU4核,15GB内存的虚拟机,每台机器有161GB存储空间(这其中包括了安装操作系统和Oracle软件的75GB本地空间,以及ASM使用的66GB共享空间)。

Screen Shot 2016-04-05 at 5.19.23 PM

以上这些都不是本文的重点,我们要介绍的是Oracle Cloud的网络设定。要知道,新创建完的虚拟机虽然可以通过SSH来访问,但是数据库默认是无法通过监听来远程访问的,必须要进行网络设定的修改。

在Oracle Cloud中网络层面的设定要涉及到以下几个概念,我们从管理界面中可以看到这些名词。

Screen-Shot-2016-04-05-at-2_15_14-PM

Security Lists 每个Security List包含了一组Oracle Compute Cloud Service实例(每个Oracle Compute Cloud Service实例可以简单地认为就是一台Oracle Cloud虚拟机,用AWS的语言说是一个EC2实例,用阿里云的语言说是一个ECS实例,以下简称为OCCS),在同一个Security List中的机器可以无障碍地通过任何网络端口进行数据交互,但是与外界的机器进行交互就要看这个Security List中对于inbound和outbound的策略定义了。Inbound策略控制了想进入这个Security List中的网络防火墙定义,Outbound策略控制了想传出这个Security List中的网络防火墙定义。 inbound和outbound的策略都有3种,分别是permit,reject和deny。 permit:允许任何数据包传输(在inbound策略中就是传入,在outbound策略中就是传出) reject:丢弃所有数据包,但是有回传信息告知数据包被丢弃 deny:丢弃所有数据包,并且不回传任何信息 如下这张图清晰地表现了不同Security List中不同的inbound和outbound策略下数据的可能流向。

Screen Shot 2016-04-05 at 6.25.13 PM

默认创建的Security List的inbound策略都是deny,下图中有关联实例的命名为oracle-cloud-enmotech/1/db_1/ora_db的Security List是正在使用的,可以看到inbound策略是deny。 Screen Shot 2016-04-09 at 2.02.44 PM

也就意味着所有的数据包不能传入到新建的机器上和数据库里。那么为什么新建的机器可以通过SSH访问,而又确实无法通过数据库监听来远程访问?这就需要提到下面这个概念-Security Rules。

不过在解释Security Rules之前,需要先解释另外两个概念,Security Applications和Security IP Lists。

Security Applications Security Application就是一组网络协议和端口的组合,从下图中可以清晰地看出来。

命名为ssh的Security Application就是tcp协议的22端口,这就是ssh默认的端口。 Screen Shot 2016-04-09 at 1.41.50 PM

命名为ora_scan_listener的Security Application就是tcp协议的1521端口,这就是数据库SCAN监听默认的端口。 Screen Shot 2016-04-09 at 1.42.13 PM

再比如命名为ora_monitor_12c的Security Application就是tcp协议的5500端口,这就是12c新的EM Express默认使用的端口。 Screen Shot 2016-04-09 at 1.43.25 PM

Security IP Lists 另外一个关键的概念,就是Security IP List,这里定义的是一组IP地址或者子网,也就是防火墙设置中在源(source)和目标(destination)中设置的值。 比如在如下图的默认设置中,public-internet就表示了所有的IP地址。

Screen-Shot-2016-04-09-at-1_51_58-PM

最后,进入Security Rules的解释。

Security Rules 一个Security Rule实际上就是一个防火墙规则,用于定义对于特定的应用访问OCCS的防火墙策略。这些防火墙是在Security List之上设置的特例。简单地说,Security List的inbound策略是deny的话,那么只有在Security Rule中明确设置可以通过的端口才被允许通过;如果Security List的inbound策略是permit的话,就不需要再设置任何Security Rule,因为所有端口都被允许了。很明显,Oracle强烈建议第一种设置方法,而不要轻易设置Security List的inbound策略是permit。

为什么新建的机器可以通过SSH访问?因为SSH的Security Rule是默认打开的(Status=Enabled)。 Screen Shot 2016-04-09 at 1.30.38 PM

解读一下,图中的各个设定的含义。 Security Application=ssh,是这个防火墙规则针对的是tcp协议的22端口; Source=public-internet,是这个防火墙规则的源定义,是一个Security IP List的值,表示任何一个IP; Destination=oracle-cloud-enmotech/1/db_1/ora_db,是这个防火墙规则的目标定义,是一个Security List的值,就是上面提到的inbound策略为deny的那个Security List,这表示在这个Security List中的所有OCCS都是目标。

再通俗易懂地解释一下,就是该防火墙定义允许任何一个IP通过22端口访问本次创建的Database之下的所有OCCS,也就是允许通过SSH来访问RAC的2个节点。

为什么无法通过数据库监听来远程访问?因为跟SCAN Listener相关的Security Rule是默认关闭的(Status=Disabled)。 Screen Shot 2016-04-09 at 1.31.19 PM

修改很简单,将状态从Disabled修改为Enabled即可。

剩下在网络设定管理界面中的另外两个名词,就比较简单了。

IP Reservations 为虚拟主机保留的公网IP,可以通过这个IP直接访问主机。 Screen_Shot_2016-04-09_at_2_07_44_PM

SSH Public Keys 登录OCCS,不是通过用户名和密码,而必须要通过SSH Key,在创建Database的过程中就会要求上传Public Key,也可以在这个界面再次更新或者上传新的Public Key。 Screen_Shot_2016-04-09_at_2_08_10_PM

结论 通过以上的设置,Oracle Cloud基本上组成了一套完善的网络防火墙体系,可以细粒度的控制哪些IP通过哪些端口访问数据库或者数据库主机。

Oracle and Cloud

Oracle的CEO拉里·埃里森(Larry Ellison)在Oracle Open World上对云计算的攻击言犹在耳。

有趣的是,云计算概念已经被我们扭曲了,现在的云计算概念几乎包括了我们要做的一切。你甚至找不到一家不宣称自己是云计算的。计算机行业是唯一一个比女性时装界还要追逐概念和潮流的行业。也许我是个笨蛋,但是我真的搞不懂他们在说些什么。到底什么是云计算呢?完全是胡扯。这很荒唐,这种愚蠢什么时候能够停下来呢?

好吧,(假如)我们也宣称我们是云计算,我们不跟云计算对着干。但是,我们真的不知道,我们每天做的有什么不同,云计算对我们顶多是个广告概念。这就是我对云计算的看法。

然而如今,我们已经可以使用Cloud中的Oracle Enterprise Edition 11g和Oracle Weblogic Server了,这是Oracle跟Amazon联手的成果。

"Oracle in the Cloud" AWS Webinar

View SlideShare presentation or Upload your own. (tags: amazon aws)

Amazon是云计算概念浪潮中的第一个实干家,EC2和S3分别了提供了前端运算和后端存储的云计算解决方案,而Oracle则是Amazon在云计算方面的第一个Partner,将Oracle Database 11g部署在Amazon EC2中,通过Oracle Secure Backup将数据库备份到Amazon S3中。这样的架构让Oracle和Amazon简直可以称为天作之合,我的意思是至少在宣传中是这样的。

云计算以其弹性、可扩展性和付费即用模式等特性在提供了灵活的高吞吐量同时又给了客户很大的成本节约空间。而使用部署在云计算中的Oracle数据库,又进一步让客户从繁琐的数据库规划和数据库备份管理的工作中解脱出来,让客户将更多的精力放在业务拓展而非IT管理上。

当然,云计算目前还存在着很多问题,最突出的就是数据安全性,将具有隐私性的数据存放在一个第三方提供的空间内,首先是否合乎法规,其次是否真的可以保证安全,这已经不单单是技术层面的问题,还牵涉到了政策法规以及消费者的心理因素。

其实,Oracle早在1995年就致力于推广NC(Netowrk Computer)的概念,并且宣称在2000年NC将大行其道,可惜这个计划在1998年的时候以失败告终。而当今的所谓云计算概念看上去正是NC的翻版,只是随着诸如网络带宽、网络速率、存储容量等近年来长足发展的技术条件的成熟,过去阻碍NC发展的障碍已经不存在了,所以这个理念又重新浮出水面并且炒得有声有色。

一个合适的概念要出生在一个合适的年代,落后注定失败,而过于超前也几乎同样意味着失败。

Larry在Open World上炮轰云计算,可能是出于他个人标新立异藐视一切的性格,对于N年后一个炒自己冷饭的概念居然甚受欢迎表达了自己的愤慨;另外则也可能是当时并没有发现很好的合作伙伴,也没有在云计算中发现属于Oracle公司自己的商业价值,甚至从类似于Amazon SimpleDB这样的云计算产品中感受到了对自己的压力。

但是,商场如战场,没有永远的盟友也没有永远的敌人,当发现了云计算中的商业价值,迅速地切入并且尝试成为领头羊无疑是明智的选择。这是Larry Ellison的个人风格,也同样是Oracle公司的企业形象,如果有可能就挑起纷争,如果有可能就抛开纷争,而所有可能的目的就是为了利益最大化