├── .gitignore ├── charts ├── README.md ├── Chart.yaml ├── values.yaml ├── .helmignore └── templates │ ├── pvc.yaml │ └── deployment.yaml ├── README.md ├── LICENSE ├── TODO.md └── migrate.sh /.gitignore: -------------------------------------------------------------------------------- 1 | setenv.sh 2 | -------------------------------------------------------------------------------- /charts/README.md: -------------------------------------------------------------------------------- 1 | # acs-aks-migrate-sample 2 | 3 | [acs-aks-migrate-sample](https://github.com/ritazh/acs-aks-migrate) is a sample app for acs-aks-migrate 4 | 5 | ## TL;DR: 6 | 7 | ```console 8 | $ helm install . -f values.yaml 9 | ``` 10 | Where `values.yaml` contains: 11 | 12 | ``` 13 | 14 | ``` -------------------------------------------------------------------------------- /charts/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | description: acs-aks-migrate sample app 3 | icon: https://github.com/kubernetes/kubernetes/blob/master/logo/logo.png 4 | name: acs-aks-migrate-sample 5 | version: 1.0.0 6 | appVersion: 1.0.0 7 | sources: 8 | - https://github.com/ritazh/acs-aks-migrate 9 | maintainers: 10 | - name: ritazh 11 | email: ritazh@microsoft.com -------------------------------------------------------------------------------- /charts/values.yaml: -------------------------------------------------------------------------------- 1 | # Default values for acs-aks-migrate-sample. 2 | # This is a YAML-formatted file. 3 | # Declare variables to be passed into your templates. 4 | replicaCount: 1 5 | ## Image for acs-aks-migrate-sample 6 | ## Will update the image and tag later 7 | image: 8 | repository: busybox 9 | ## Optional parameter for migrate or not 10 | # enablemigration: false 11 | disk: 12 | name: 13 | uri: 14 | -------------------------------------------------------------------------------- /charts/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *~ 18 | # Various IDEs 19 | .project 20 | .idea/ 21 | *.tmproj -------------------------------------------------------------------------------- /charts/templates/pvc.yaml: -------------------------------------------------------------------------------- 1 | {{- if not .Values.enablemigration }} 2 | apiVersion: v1 3 | kind: PersistentVolumeClaim 4 | metadata: 5 | annotations: 6 | provisioner: kubernetes.io/azure-disk 7 | storageclass.beta.kubernetes.io/is-default-class: 'true' 8 | volume.beta.kubernetes.io/storage-provisioner: kubernetes.io/azure-disk 9 | name: myclaim 10 | spec: 11 | accessModes: 12 | - ReadWriteOnce 13 | resources: 14 | requests: 15 | storage: 50Gi 16 | storageClassName: managed-premium 17 | {{- end }} 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # acs-aks-migrate 2 | ⚒ Script to migrate from ACS Kubernetes clusters to ACS (RP v2 [regions](https://github.com/Azure/ACS/blob/master/acs_regional_avilability) ) or AKS cluster 3 | 4 | __USE IT AT YOUR OWN RISK - DO NOT USE IN PRODUCTION__ 5 | 6 | * Set the following environment variables 7 | 8 | ```bash 9 | AZURE_SUBSCRIPTION_ID= 10 | AZURE_TENANT_ID= 11 | AZURE_CLIENT_ID= 12 | AZURE_CLIENT_SECRET= 13 | DESTINATION_CLUSTERNAME= 14 | DESTINATION_ACS_RESOURCEGROUP= 15 | DESTINATION_STORAGEACCOUNT= 16 | DESTINATION_STORAGEACCOUNT_CONTAINER= 17 | DESTINATION_CLUSTERNAME_LOCATION=westus2 18 | DESTINATION_MANAGED_DISK= 19 | SOURCE_BLOB= 20 | SOURCE_STORAGEACCOUNT_CONTAINER= 21 | #SOURCE_SNAPSHOT='' 22 | SOURCE_STORAGEACCOUNT= 23 | SOURCE_STORAGEACCOUNT_KEY= 24 | ``` 25 | * Run script 26 | 27 | ```bash 28 | bash migrate.sh 29 | ``` 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Rita Zhang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /charts/templates/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: extensions/v1beta1 2 | kind: Deployment 3 | metadata: 4 | labels: 5 | run: busybox 6 | name: busybox 7 | spec: 8 | replicas: {{ .Values.replicaCount }} 9 | selector: 10 | matchLabels: 11 | run: busybox 12 | strategy: 13 | rollingUpdate: 14 | maxSurge: 1 15 | maxUnavailable: 1 16 | type: RollingUpdate 17 | template: 18 | metadata: 19 | creationTimestamp: null 20 | labels: 21 | run: busybox 22 | spec: 23 | containers: 24 | - args: 25 | - sleep 60000 26 | command: 27 | - /bin/sh 28 | - -c 29 | - -- 30 | image: {{ .Values.image.repository }} 31 | imagePullPolicy: Always 32 | name: busybox 33 | resources: {} 34 | terminationMessagePath: /dev/termination-log 35 | terminationMessagePolicy: File 36 | volumeMounts: 37 | - mountPath: /data 38 | name: mydisk 39 | dnsPolicy: ClusterFirst 40 | restartPolicy: Always 41 | schedulerName: default-scheduler 42 | securityContext: {} 43 | terminationGracePeriodSeconds: 30 44 | volumes: 45 | - name: mydisk 46 | {{- if .Values.enablemigration }} 47 | azureDisk: 48 | cachingMode: ReadWrite 49 | diskName: {{ .Values.disk.name }} 50 | diskURI: {{ .Values.disk.uri }} 51 | fsType: ext4 52 | kind: Managed 53 | readOnly: false 54 | {{- else }} 55 | persistentVolumeClaim: 56 | claimName: myclaim 57 | {{- end }} 58 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | 2 | # Export k8s resources 3 | 4 | in: kubectl + kubeconfig configured 5 | out: folder of yaml files 6 | 7 | * kubectl export? 8 | 9 | ## Get volume info from k8s 10 | 11 | in: kubectl + kubeconfig configured 12 | out: list of VHD paths 13 | 14 | * Get Pods (and/or Deployments/ReplicationControllers/etc) 15 | * Get PersistentVolumeClaims 16 | * Get PersistentVolumes 17 | * Get VHD paths 18 | 19 | ## Create new ACS/AKS cluster 20 | 21 | in: az configured 22 | out: new cluster created, RG name for new cluster (dynamically created for RPv2 & AKS) 23 | 24 | * Create RG for target cluster 25 | * az acs/aks create 26 | 27 | ## Convert VHD to managed disks 28 | 29 | in: az configured, list of VHDs, target RG 30 | out: managed disks created in target RG 31 | 32 | * Warning: Stop disk writes OR sync later 33 | * Create blob snapshot of VHDs 34 | * Detect region move (based on VHD storage acct and target RG name) 35 | * Create dynamically named storage account to copy blob (if moving regions) 36 | * Copy snapshot to new blob (if moving regions) 37 | * Copy blob to another region (if moving regions) 38 | * Create managed disks from snapshot (or copied blob if moving regions) in new RG 39 | * Cleanup temp files (copied blob if moving regions, remove snapshots) 40 | 41 | ## Deploy k8s to new cluster 42 | 43 | in: kubectl + kubeconfig for new cluster, yaml folder path 44 | out: apps running on new cluster, next steps 45 | 46 | * Modify YAML to use managed disks 47 | * Create PV for managed disk (if needed) 48 | * Add label to PVs 49 | * Add selector to PVCs 50 | * kubectl apply yaml files 51 | * Show info for next steps: 52 | * Sync data from VHD (only if writes were not stopped) 53 | * Point traffic to new cluster 54 | * Cleanup steps (snapshots, old vhd's, etc) -------------------------------------------------------------------------------- /migrate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | echo $(date +"%F %T%z") "- Starting script migrate.sh" 4 | 5 | # Get values for this script 6 | echo "AZURE_SUBSCRIPTION_ID: $AZURE_SUBSCRIPTION_ID" 7 | echo "AZURE_TENANT_ID: $AZURE_TENANT_ID" 8 | echo "AZURE_CLIENT_ID: $AZURE_CLIENT_ID" 9 | echo "AZURE_CLIENT_SECRET: $AZURE_CLIENT_SECRET" 10 | echo "DESTINATION_CLUSTERNAME: $DESTINATION_CLUSTERNAME" 11 | echo "DESTINATION_ACS_RESOURCEGROUP: $DESTINATION_ACS_RESOURCEGROUP" 12 | echo "DESTINATION_STORAGEACCOUNT: $DESTINATION_STORAGEACCOUNT" 13 | echo "DESTINATION_STORAGEACCOUNT_CONTAINER: $DESTINATION_STORAGEACCOUNT_CONTAINER" 14 | echo "DESTINATION_MANAGED_DISK: $DESTINATION_MANAGED_DISK" 15 | echo "SOURCE_BLOB: $SOURCE_BLOB" 16 | echo "SOURCE_STORAGEACCOUNT_CONTAINER: $SOURCE_STORAGEACCOUNT_CONTAINER" 17 | #echo "SOURCE_SNAPSHOT: $SOURCE_SNAPSHOT" 18 | echo "SOURCE_STORAGEACCOUNT: $SOURCE_STORAGEACCOUNT" 19 | echo "SOURCE_STORAGEACCOUNT_KEY: $SOURCE_STORAGEACCOUNT_KEY" 20 | 21 | SSHKEY_FILEPATH=~/.ssh/acsmigrate 22 | 23 | if [ -z "$AZURE_SUBSCRIPTION_ID" ]; then 24 | echo "Error: Missing env var for AZURE_SUBSCRIPTION_ID" 25 | exit 0 26 | fi 27 | 28 | if [ -z "$AZURE_TENANT_ID" ]; then 29 | echo "Error: Missing env var for AZURE_TENANT_ID" 30 | exit 0 31 | fi 32 | 33 | if [ -z "$AZURE_CLIENT_ID" ]; then 34 | echo "Error: Missing env var for AZURE_CLIENT_ID" 35 | exit 0 36 | fi 37 | 38 | if [ -z "$AZURE_CLIENT_SECRET" ]; then 39 | echo "Error: Missing env var for AZURE_CLIENT_SECRET" 40 | exit 0 41 | fi 42 | 43 | if [ -z "$DESTINATION_CLUSTERNAME" ]; then 44 | echo "Error: Missing env var for DESTINATION_CLUSTERNAME" 45 | exit 0 46 | fi 47 | 48 | if [ -z "$DESTINATION_ACS_RESOURCEGROUP" ]; then 49 | echo "Error: Missing env var for DESTINATION_ACS_RESOURCEGROUP" 50 | exit 0 51 | fi 52 | 53 | if [ -z "$DESTINATION_STORAGEACCOUNT" ]; then 54 | echo "Error: Missing env var for DESTINATION_STORAGEACCOUNT" 55 | exit 0 56 | fi 57 | 58 | if [ -z "$DESTINATION_STORAGEACCOUNT_CONTAINER" ]; then 59 | echo "Error: Missing env var for DESTINATION_STORAGEACCOUNT_CONTAINER" 60 | exit 0 61 | fi 62 | 63 | if [ -z "$DESTINATION_MANAGED_DISK" ]; then 64 | echo "Error: Missing env var for DESTINATION_MANAGED_DISK" 65 | exit 0 66 | fi 67 | 68 | if [ -z "$SOURCE_BLOB" ]; then 69 | echo "Error: Missing env var for SOURCE_BLOB" 70 | exit 0 71 | fi 72 | 73 | if [ -z "$SOURCE_STORAGEACCOUNT_CONTAINER" ]; then 74 | echo "Error: Missing env var for SOURCE_STORAGEACCOUNT_CONTAINER" 75 | exit 0 76 | fi 77 | 78 | DESTINATION_RESOURCEGROUP='$DESTINATION_ACS_RESOURCEGROUP_$DESTINATION_CLUSTERNAME_$DESTINATION_CLUSTERNAME_LOCATION' 79 | # if [ -z "$SOURCE_SNAPSHOT" ]; then 80 | # echo "Error: Missing env var for SOURCE_SNAPSHOT" 81 | # exit 0 82 | # fi 83 | 84 | if [ -z "$SOURCE_STORAGEACCOUNT" ]; then 85 | echo "Error: Missing env var for SOURCE_STORAGEACCOUNT" 86 | exit 0 87 | fi 88 | 89 | if [ -z "$SOURCE_STORAGEACCOUNT_KEY" ]; then 90 | echo "Error: Missing env var for SOURCE_STORAGEACCOUNT_KEY" 91 | exit 0 92 | fi 93 | 94 | if [ -f $SSHKEY_FILEPATH ]; then 95 | echo "ssh key $SSHKEY_FILEPATH for migration already exists...skip creating ssh key" 96 | else 97 | echo "Generating new ssh key $SSHKEY_FILEPATH" 98 | ssh-keygen -t rsa -b 4096 -C "acs@migrate.com" -f $SSHKEY_FILEPATH 99 | fi 100 | 101 | # Assuming KUBECONFIG is set to source cluster 102 | DISK_URIS=$(kubectl get pv -o json | jq -r '.items[].spec.azureDisk.diskURI' | grep http) 103 | 104 | echo "Creating new destination cluster $DESTINATION_CLUSTERNAME with premium managed disks " 105 | az acs create -g $z -n $DESTINATION_CLUSTERNAME --orchestrator-type Kubernetes --agent-count 2 --agent-osdisk-size 100 --agent-vm-size Standard_DS2_v2 --agent-storage-profile ManagedDisks --master-storage-profile ManagedDisks --ssh-key-value $SSHKEY_FILEPATH --dns-prefix azure-$DESTINATION_CLUSTERNAME --location $DESTINATION_CLUSTERNAME_LOCATION --service-principal $AZURE_CLIENT_ID --client-secret $AZURE_CLIENT_SECRET 106 | 107 | echo "Creating storage account $DESTINATION_STORAGEACCOUNT" 108 | az storage account create -n $DESTINATION_STORAGEACCOUNT -g '$DESTINATION_RESOURCEGROUP_$DESTINATION_RESOURCEGROUP' -l $DESTINATION_CLUSTERNAME_LOCATION --sku Standard_LRS 109 | DESTINATION_STORAGEACCOUNT_KEY=$(az storage account keys list -g '$DESTINATION_RESOURCEGROUP_$DESTINATION_RESOURCEGROUP' -n $DESTINATION_STORAGEACCOUNT | jq -r '.[0].value') 110 | echo "Storage account key: $DESTINATION_STORAGEACCOUNT_KEY" 111 | 112 | echo "Creating container $DESTINATION_STORAGEACCOUNT_CONTAINER" 113 | az storage container create --name $DESTINATION_STORAGEACCOUNT_CONTAINER --account-key $DESTINATION_STORAGEACCOUNT_KEY --account-name $DESTINATION_STORAGEACCOUNT 114 | 115 | echo "Creating snapshot for vhd $SOURCE_BLOB" 116 | SOURCE_SNAPSHOT=$(az storage blob snapshot -c $SOURCE_STORAGEACCOUNT_CONTAINER -n $SOURCE_BLOB --account-name $SOURCE_STORAGEACCOUNT --account-key $SOURCE_STORAGEACCOUNT_KEY | jq -r '.snapshot') 117 | 118 | echo "Copying vhd snapshot $SOURCE_BLOB $SOURCE_SNAPSHOT" 119 | az storage blob copy start --source-blob $SOURCE_BLOB --source-container $SOURCE_STORAGEACCOUNT_CONTAINER --source-snapshot $SOURCE_SNAPSHOT -b $SOURCE_BLOB -c $DESTINATION_STORAGEACCOUNT_CONTAINER --source-account-name $SOURCE_STORAGEACCOUNT --source-account-key $SOURCE_STORAGEACCOUNT_KEY --account-name $DESTINATION_STORAGEACCOUNT --account-key $DESTINATION_STORAGEACCOUNT_KEY 120 | 121 | echo "Creating Managed Disk from vhd $SOURCE_BLOB" 122 | az disk create -n $DESTINATION_MANAGED_DISK -g '$DESTINATION_RESOURCEGROUP_$DESTINATION_RESOURCEGROUP' --source http://$DESTINATION_STORAGEACCOUNT.blob.core.windows.net/$DESTINATION_STORAGEACCOUNT_CONTAINER/$SOURCE_BLOB 123 | 124 | echo $(date +"%F %T%z") " - Script complete" 125 | --------------------------------------------------------------------------------