- Significant enhancements for AKS:
  - Fully support creating clusters with managed identities. This is recommended and the new default, compared to the older method of using service principals to control cluster resources.
  - Support creating clusters using VM scalesets for the cluster nodes. This is recommended and the new default, compared to using individual VMs.
  - Support private clusters.
  - Support node autoscaling for agent pools backed by VM scalesets.
  - Support spot (low-priority) nodes for agent pools backed by VM scalesets.
  - New methods for the `az_kubernetes_service` class, for managing agent pools: `get_agent_pool`, `create_agent_pool`, `delete_agent_pool` and `list_agent_pools`. Creating new agent pools requires VM scalesets, as mentioned above.
- New `agent_pool` function to supply the parameters for a _single_ AKS agent pool.
- The functions to call external tools (`call_docker`, `call_docker_compose`, `call_kubernetes` and `call_helm`) now use the value of the system option `azure_containers_tool_echo` to determine whether to echo output to the screen. If this is unset, the fallback is `TRUE` (as in previous versions).

Closes #9, closes #10, closes #11
This commit is contained in:
Hong Ooi 2020-04-28 21:52:05 +10:00 коммит произвёл GitHub
Родитель 64df96cea0
Коммит 15b0106943
38 изменённых файлов: 786 добавлений и 263 удалений

Просмотреть файл

@ -1,6 +1,6 @@
Package: AzureContainers
Title: Interface to 'Container Instances', 'Docker Registry' and 'Kubernetes' in 'Azure'
Version: 1.2.1
Version: 1.2.1.9000
Authors@R: c(
person("Hong", "Ooi", , "hongooi@microsoft.com", role = c("aut", "cre")),
person("Bill", "Liang", role = "ctb", comment = "Assistance debugging MMLS on Kubernetes"),
@ -26,5 +26,5 @@ Suggests:
knitr,
testthat,
uuid
Roxygen: list(markdown=TRUE)
RoxygenNote: 6.1.1
Roxygen: list(markdown=TRUE, r6=FALSE, old_usage=TRUE)
RoxygenNote: 7.1.0

Просмотреть файл

@ -6,6 +6,7 @@ export(aci)
export(aci_creds)
export(aci_ports)
export(acr)
export(agent_pool)
export(aks)
export(aks_pools)
export(call_docker)

12
NEWS.md
Просмотреть файл

@ -1,3 +1,15 @@
# AzureContainers 1.2.1.9000
- Significant enhancements for AKS:
- Fully support creating clusters with managed identities. This is recommended and the new default, compared to the older method of using service principals to control cluster resources.
- Support creating clusters using VM scalesets for the cluster nodes. This is recommended and the new default, compared to using individual VMs.
- Support private clusters.
- Support node autoscaling for agent pools backed by VM scalesets.
- Support spot (low-priority) nodes for agent pools backed by VM scalesets.
- New methods for the `az_kubernetes_service` class, for managing agent pools: `get_agent_pool`, `create_agent_pool`, `delete_agent_pool` and `list_agent_pools`. Creating new agent pools requires VM scalesets, as mentioned above.
- New `agent_pool` function to supply the parameters for a _single_ AKS agent pool.
- The functions to call external tools (`call_docker`, `call_docker_compose`, `call_kubernetes` and `call_helm`) now use the value of the system option `azure_containers_tool_echo` to determine whether to echo output to the screen. If this is unset, the fallback is `TRUE` (as in previous versions).
# AzureContainers 1.2.1
- Fix a bug where `call_docker_compose` could be checking for the wrong binary.

Просмотреть файл

@ -13,7 +13,7 @@
#' @section Arguments:
#' - `name`: The name of the container registry.
#' - `location`: The location/region in which to create the container registry. Defaults to this resource group's location.
#' - `admin_user_enabled`: Whether to enable the Admin user. Currently this must be TRUE to allow Docker to access the registry.
#' - `admin_user_enabled`: Whether to enable the Admin user. Currently this must be `TRUE` for ACI to pull from the registry.
#' - `sku`: Either "Basic", "Standard" (the default) or "Premium".
#' - `wait`: Whether to wait until the ACR resource provisioning is complete.
#' - `...`: Other named arguments to pass to the [az_resource] initialization function.

Просмотреть файл

@ -9,9 +9,10 @@
#' ```
#' create_aks(name, location = self$location,
#' dns_prefix = name, kubernetes_version = NULL,
#' enable_rbac = FALSE, agent_pools = list(),
#' enable_rbac = FALSE, agent_pools = agent_pool("pool1", 3),
#' login_user = "", login_passkey = "",
#' cluster_service_principal = NULL, managed_identity = FALSE,
#' cluster_service_principal = NULL, managed_identity = TRUE,
#' private_cluster = FALSE,
#' properties = list(), ..., wait = TRUE)
#' ```
#' @section Arguments:
@ -19,11 +20,12 @@
#' - `location`: The location/region in which to create the service. Defaults to this resource group's location.
#' - `dns_prefix`: The domain name prefix to use for the cluster endpoint. The actual domain name will start with this argument, followed by a string of pseudorandom characters.
#' - `kubernetes_version`: The Kubernetes version to use. If not specified, uses the most recent version of Kubernetes available.
#' - `enable_rbac`: Whether to enable role-based access controls.
#' - `agent_pools`: A list of pool specifications. See 'Details'.
#' - `enable_rbac`: Whether to enable Kubernetes role-based access controls (which is distinct from Azure AD RBAC).
#' - `agent_pools`: The pool specification(s) for the cluster. See 'Details'.
#' - `login_user,login_passkey`: Optionally, a login username and public key (on Linux). Specify these if you want to be able to ssh into the cluster nodes.
#' - `cluster_service_principal`: The service principal (client) that AKS will use to manage the cluster resources. This should be a list, with the first component being the client ID and the second the client secret. If not supplied, a new service principal will be created (requires an interactive session).
#' - `managed_identity`: Whether the cluster should have a managed identity assigned to it. This is currently in preview; see the [Microsoft Docs page](https://docs.microsoft.com/en-us/azure/aks/use-managed-identity) for enabling this feature.
#' - `cluster_service_principal`: The service principal that AKS will use to manage the cluster resources. This should be a list, with the first component being the client ID and the second the client secret. If not supplied, a new service principal will be created (requires an interactive session). Ignored if `managed_identity=TRUE`, which is the default.
#' - `managed_identity`: Whether the cluster should have a managed identity assigned to it. If `FALSE`, a service principal will be used to manage the cluster's resources; see 'Details' below.
#' - `private_cluster`: Whether this cluster is private (not visible from the public Internet). A private cluster is accessible only to hosts on its virtual network.
#' - `properties`: A named list of further Kubernetes-specific properties to pass to the initialization function.
#' - `wait`: Whether to wait until the AKS resource provisioning is complete. Note that provisioning a Kubernetes cluster can take several minutes.
#' - `...`: Other named arguments to pass to the initialization function.
@ -31,15 +33,19 @@
#' @section Details:
#' An AKS resource is a Kubernetes cluster hosted in Azure. See the [documentation for the resource][aks] for more information. To work with the cluster (deploy images, define and start services, etc) see the [documentation for the cluster endpoint][kubernetes_cluster].
#'
#' To specify the agent pools for the cluster, it is easiest to use the [aks_pools] function. This takes as arguments the name(s) of the pools, the number of nodes, the VM size(s) to use, and the operating system (Windows or Linux) to run on the VMs.
#' The nodes for an AKS cluster are organised into _agent pools_, also known as _node pools_, which are homogenous groups of virtual machines. To specify the details for a single agent pool, use the `agent_pool` function, which returns an S3 object of that class. To specify the details for multiple pools, you can supply a list of such objects, or a single call to the `aks_pools` function; see the examples below. Note that `aks_pools` is older, and does not support all the possible parameters for an agent pool.
#'
#' By default, the password for a newly-created service principal will expire after one year. You can run the `update_service_password` method of the AKS object to reset/update the password before it expires.
#' Of the agent pools in a cluster, at least one must be a _system pool_, which is used to host critical system pods such as CoreDNS and tunnelfront. If you specify more than one pool, the first pool will be treated as the system pool. Note that there are certain [extra requirements](https://docs.microsoft.com/en-us/azure/aks/use-system-pools) for the system pool.
#'
#' An AKS cluster requires an identity to manage the low-level resources it uses, such as virtual machines and networks. The default and recommended method is to use a _managed identity_, in which all the details of this process are handled by AKS. In AzureContainers version 1.2.1 and older, a _service principal_ was used instead, which is an older and less automated method. By setting `managed_identity=FALSE`, you can continue using a service principal instead of a managed identity.
#'
#' One thing to be aware of with service principals is that they have a secret password that will expire eventually. By default, the password for a newly-created service principal will expire after one year. You should run the `update_service_password` method of the AKS object to reset/update the password before it expires.
#'
#' @section Value:
#' An object of class `az_kubernetes_service` representing the service.
#'
#' @seealso
#' [get_aks], [delete_aks], [list_aks], [aks_pools]
#' [get_aks], [delete_aks], [list_aks], [agent_pool], [aks_pools]
#'
#' [az_kubernetes_service]
#'
@ -57,10 +63,20 @@
#' get_subscription("subscription_id")$
#' get_resource_group("rgname")
#'
#' rg$create_aks("mycluster", agent_pools=aks_pools("pool1", 5))
#' rg$create_aks("mycluster", agent_pools=agent_pool("pool1", 5))
#'
#' # GPU-enabled cluster
#' rg$create_aks("mygpucluster", agent_pools=aks_pools("pool1", 5, size="Standard_NC6s_v3"))
#' rg$create_aks("mygpucluster", agent_pools=agent_pool("pool1", 5, size="Standard_NC6s_v3"))
#'
#' # multiple agent pools
#' rg$create_aks("mycluster", agent_pools=list(
#' agent_pool("pool1", 2),
#' agent_pool("pool2", 3, size="Standard_NC6s_v3")
#' ))
#'
#' # deprecated alternative for multiple pools
#' rg$create_aks("mycluster",
#' agent_pools=aks_pools(c("pool1", "pool2"), c(2, 3), c("Standard_DS2_v2", "Standard_NC6s_v3")))
#'
#' }
NULL
@ -205,35 +221,59 @@ add_aks_methods <- function()
function(name, location=self$location,
dns_prefix=name, kubernetes_version=NULL,
login_user="", login_passkey="",
enable_rbac=FALSE, agent_pools=list(),
enable_rbac=FALSE, agent_pools=agent_pool("pool1", 3),
cluster_service_principal=NULL,
managed_identity=FALSE,
managed_identity=TRUE, private_cluster=FALSE,
properties=list(), ..., wait=TRUE)
{
if(is_empty(kubernetes_version))
kubernetes_version <- tail(self$list_kubernetes_versions(), 1)
# hide from CRAN check
find_app_creds <- get("find_app_creds", getNamespace("AzureContainers"))
cluster_service_principal <- find_app_creds(cluster_service_principal, name, location, self$token)
# figure out how to handle managing resources: either identity, or SP
if(managed_identity)
{
identity <- list(type="systemAssigned")
sp_profile <- NULL
}
else
{
identity <- NULL
# hide from CRAN check
find_app_creds <- get("find_app_creds", getNamespace("AzureContainers"))
cluster_service_principal <- find_app_creds(cluster_service_principal, name, location, self$token)
if(is.null(cluster_service_principal[[2]]))
stop("Must provide a service principal with a secret password", call.=FALSE)
sp_profile <- list(
clientId=cluster_service_principal[[1]],
secret=cluster_service_principal[[2]]
)
}
if(inherits(agent_pools, "agent_pool"))
agent_pools <- list(unclass(agent_pools))
else if(is.list(agent_pools) && all(sapply(agent_pools, inherits, "agent_pool")))
agent_pools <- lapply(agent_pools, unclass)
# 1st agent pool is system
agent_pools[[1]]$mode <- "System"
props <- list(
kubernetesVersion=kubernetes_version,
dnsPrefix=dns_prefix,
agentPoolProfiles=agent_pools,
enableRBAC=enable_rbac,
servicePrincipalProfile=list(
clientId=cluster_service_principal[[1]],
secret=cluster_service_principal[[2]]
)
enableRBAC=enable_rbac
)
if(is.null(props$servicePrincipalProfile$secret))
stop("Must provide a service principal with a secret password", call.=FALSE)
if(private_cluster)
{
props$apiServerAccessProfile <- list(enablePrivateCluster=private_cluster)
props$networkProfile <- list(loadBalancerSku="standard")
}
identity <- if(managed_identity)
list(type="systemAssigned")
else NULL
if(!is.null(sp_profile))
props$servicePrincipalProfile <- sp_profile
if(login_user != "" && login_passkey != "")
props$linuxProfile <- list(

2
R/az_agent_pool.R Normal file
Просмотреть файл

@ -0,0 +1,2 @@
# stub class, may be expanded later
az_agent_pool <- R6::R6Class("az_agent_pool", inherit=AzureRMR::az_resource)

Просмотреть файл

@ -68,9 +68,18 @@ public=list(
{
if(is_aks(principal))
{
tenant <- self$token$tenant
gr <- graph_login(tenant, ...)
principal <- gr$get_app(principal$properties$servicePrincipalProfile$clientId)
clientid <- principal$properties$servicePrincipalProfile$clientId
if(clientid == "msi")
{
ident <- principal$properties$identityProfile
principal <- ident[[1]]$objectId
}
else
{
tenant <- self$token$tenant
principal <- graph_login(tenant, ...)$get_app(clientid)
}
}
super$add_role_assignment(principal, role, scope)
},

Просмотреть файл

@ -7,6 +7,10 @@
#' The following methods are available, in addition to those provided by the [AzureRMR::az_resource] class:
#' - `new(...)`: Initialize a new AKS object.
#' - `get_cluster(config, role)`: Return an object representing the Docker registry endpoint.
#' - `get_agent_pool(pollname)`: Returns an object of class `az_agent_pool` representing an agent pool. This class inherits from [AzureRMR::az_resource]; it currently has no extra methods, but these may be added in the future.
#' - `list_agent_pools()`: Returns a list of agent pool objects.
#' - `create_agent_pool(poolname, ..., wait=FALSE)`: Creates a new agent pool. See the [agent_pool] function for further arguments to this method.
#' - `delete_agent_pool(poolname, confirm=TRUE. wait=FALSE)`: Deletes an agent pool.
#' - `list_cluster_resources()`: Returns a list of all the Azure resources managed by the cluster.
#' - `update_aad_password(name=NULL, duration=NULL, ...)`: Update the password for Azure Active Directory integration, returning the new password invisibly. See 'Updating credentials' below.
#' - `update_service_password(name=NULL, duration=NULL, ...)`: Update the password for the service principal used to manage the cluster resources, returning the new password invisibly. See 'Updating credentials' below.
@ -22,9 +26,9 @@
#' - `role`: This can be `"User"` (the default) or `"Admin"`.
#'
#' @section Updating credentials:
#' An AKS cluster requires at least one, and possibly three, service principals. The first service principal is used to manage the resources used by the cluster: the VMs, networking resources, virtual disks, etc. The other two are used for AAD integration. These service principals have secret passwords, which have to be refreshed as they expire.
#' An AKS resource can have up to three service principals associated with it. Two of these are for Azure Active Directory (AAD) integration. The third is used to manage the subsidiary resources (VMs, networks, disks, etc) used by the cluster, if it doesn't have a service identity.
#'
#' The `update_aad_password()` and `update_service_password()` methods let you refresh the passwords for the cluster's service principals. Their arguments are:
#' Any service principals used by the AKS resource will have secret passwords, which have to be refreshed as they expire. The `update_aad_password()` and `update_service_password()` methods let you refresh the passwords for the cluster's service principals. Their arguments are:
#'
#' - `name`: An optional friendly name for the password.
#' - `duration`: The duration for which the new password is valid. Defaults to 2 years.
@ -53,7 +57,14 @@
#' # get the cluster endpoint
#' kubclus <- myaks$get_cluster()
#'
#' # refresh the service principal password
#' # list of agent pools
#' myaks$list_agent_pools()
#'
#' # create a new agent pool, then delete it
#' pool <- myaks$create_agent_pool("pool2", 3, size="Standard_DS3_v2")
#' pool$delete()
#'
#' # refresh the service principal password (mostly for legacy clusters without a managed identity)
#' myaks$update_service_password()
#'
#' # refresh the service principal password, using custom credentials to authenticate with MS Graph
@ -104,6 +115,43 @@ public=list(
kubernetes_cluster(config=config)
},
get_agent_pool=function(poolname)
{
az_agent_pool$new(self$token, self$subscription, resource_group=self$resource_group,
type="Microsoft.ContainerService/managedClusters",
name=file.path(self$name, "agentPools", poolname),
api_version=self$get_api_version())
},
create_agent_pool=function(poolname, ..., wait=FALSE)
{
pool <- agent_pool(name=poolname, ...)
az_agent_pool$new(self$token, self$subscription, resource_group=self$resource_group,
type="Microsoft.ContainerService/managedClusters",
name=file.path(self$name, "agentPools", poolname),
properties=pool[-1],
api_version=self$get_api_version(),
wait=wait)
},
delete_agent_pool=function(poolname, confirm=TRUE, wait=FALSE)
{
az_agent_pool$new(self$token, self$subscription, resource_group=self$resource_group,
type="Microsoft.ContainerService/managedClusters",
name=file.path(self$name, "agentPools", poolname),
deployed_properties=list(NULL),
api_version=self$get_api_version())$
delete(confirm=confirm, wait=wait)
},
list_agent_pools=function()
{
cont <- self$do_operation("agentPools")
api_version <- self$get_api_version()
lapply(get_paged_list(cont, self$token), function(parms)
az_agent_pool$new(self$token, self$subscription, deployed_properties=parms, api_version=api_version))
},
list_cluster_resources=function()
{
clusrgname <- self$properties$nodeResourceGroup
@ -129,6 +177,9 @@ public=list(
update_service_password=function(name=NULL, duration=NULL, ...)
{
prof <- self$properties$servicePrincipalProfile
if(prof$clientId == "msi")
stop("Cluster uses service identity for managing resources", call.=FALSE)
app <- graph_login(self$token$tenant, ...)$get_app(prof$clientId)
app$add_password(name, duration)
prof$secret <- app$password
@ -146,15 +197,86 @@ public=list(
#' @param count The number of nodes per pool.
#' @param size The VM type (size) to use for the pool. To see a list of available VM sizes, use the [list_vm_sizes] method for the resource group or subscription classes.
#' @param os The operating system to use for the pool. Can be "Linux" or "Windows".
#' @param disksize The OS disk size in gigabytes for each node in the pool. A value of 0 means to use the default disk size for the VM type.
#' @param use_scaleset Whether to use a VM scaleset instead of individual VMs for this pool. A scaleset offers greater flexibility than individual VMs, and is the recommended method of creating an agent pool.
#' @param low_priority If this pool uses a scaleset, whether it should be made up of spot (low-priority) VMs. A spot VM pool is cheaper, but is subject to being evicted to make room for other, higher-priority workloads. Ignored if `use_scaleset=FALSE`.
#' @param cluster_autoscale The autoscaling parameters for the pool. This can be either `FALSE`, meaning autoscaling is disabled, or a vector of 2 numbers giving the minimum and maximum size of the agent pool. Ignored if `use_scaleset=FALSE`.
#' @param ... Other named arguments, to be used as parameters for the agent pool.
#'
#' @details
#' `agent_pool` is a convenience function to simplify the task of specifying the agent pool for a Kubernetes cluster.
#'
#' @return
#' An object of class `agent_pool`, suitable for passing to the `create_aks` constructor method.
#'
#' @seealso
#' [create_aks], [list_vm_sizes]
#'
#' [Agent pool parameters on Microsoft Docs](https://docs.microsoft.com/en-us/rest/api/aks/managedclusters/createorupdate#managedclusteragentpoolprofile)
#'
#' @examples
#' # pool of 5 Linux GPU-enabled VMs
#' agent_pool("pool1", 5, size="Standard_NC6s_v3")
#'
#' # pool of 3 Windows Server VMs, 500GB disk size each
#' agent_pool("pool1", 3, os="Windows", disksize=500)
#'
#' # enable cluster autoscaling, with a minimum of 1 and maximum of 10 nodes
#' agent_pool("pool1", 5, cluster_autoscale=c(1, 10))
#'
#' # use individual VMs rather than scaleset
#' agent_pool("vmpool1", 3, use_scaleset=FALSE)
#'
#' @export
agent_pool <- function(name, count, size="Standard_DS2_v2", os="Linux", disksize=0,
use_scaleset=TRUE, low_priority=FALSE, cluster_autoscale=FALSE, ...)
{
parms <- list(
name=name,
count=as.integer(count),
vmSize=size,
osType=os,
osDiskSizeGB=disksize,
type=if(use_scaleset) "VirtualMachineScaleSets" else "AvailabilitySet"
)
if(use_scaleset)
{
if(is.numeric(cluster_autoscale) && length(cluster_autoscale) == 2)
{
parms$enableAutoScaling <- TRUE
parms$minCount <- min(cluster_autoscale)
parms$maxCount <- max(cluster_autoscale)
}
if(low_priority)
parms$scaleSetPriority <- "spot"
}
extras <- list(...)
if(!is_empty(extras))
parms <- utils::modifyList(parms, extras)
structure(parms, class="agent_pool")
}
#' Vectorised utility function for specifying Kubernetes agent pools
#'
#' @param name The name(s) of the pool(s).
#' @param count The number of nodes per pool.
#' @param size The VM type (size) to use for the pool. To see a list of available VM sizes, use the [list_vm_sizes] method for the resource group or subscription classes.
#' @param os The operating system to use for the pool. Can be "Linux" or "Windows".
#'
#' @details
#' This is a convenience function to simplify the task of specifying the agent pool for a Kubernetes cluster. You can specify multiple pools by providing vectors as input arguments; any scalar inputs will be replicated to match.
#'
#' `aks_pools` is deprecated; please use [agent_pool] going forward.
#'
#' @return
#' A list of lists, suitable for passing to the `create_aks` constructor method.
#'
#' @seealso
#' [list_vm_sizes]
#' [list_vm_sizes], [agent_pool]
#'
#' @examples
#' # 1 pool of 5 Linux VMs
@ -174,4 +296,3 @@ aks_pools <- function(name, count, size="Standard_DS2_v2", os="Linux")
pool_df$name <- make.unique(pool_df$name, sep="")
lapply(seq_len(nrow(pool_df)), function(i) unclass(pool_df[i, ]))
}

Просмотреть файл

@ -42,7 +42,7 @@
#'
#' }
#' @export
call_docker <- function(cmd="", ..., echo=TRUE)
call_docker <- function(cmd="", ..., echo=getOption("azure_containers_tool_echo", TRUE))
{
if(.AzureContainers$docker == "")
stop("docker binary not found", call.=FALSE)
@ -60,6 +60,7 @@ call_docker <- function(cmd="", ..., echo=TRUE)
realcmd <- cmd
}
echo <- as.logical(echo)
val <- processx::run(dockercmd, strsplit(realcmd, " ", fixed=TRUE)[[1]], ..., echo=echo)
val$cmdline <- paste("docker", cmd)
invisible(val)
@ -92,7 +93,7 @@ call_docker <- function(cmd="", ..., echo=TRUE)
#'
#' [Docker-compose command line reference](https://docs.docker.com/compose/)
#' @export
call_docker_compose <- function(cmd="", ..., echo=TRUE)
call_docker_compose <- function(cmd="", ..., echo=getOption("azure_containers_tool_echo", TRUE))
{
if(.AzureContainers$dockercompose == "")
stop("docker-compose binary not found", call.=FALSE)
@ -110,6 +111,7 @@ call_docker_compose <- function(cmd="", ..., echo=TRUE)
realcmd <- cmd
}
echo <- as.logical(echo)
val <- processx::run(dcmpcmd, strsplit(realcmd, " ", fixed=TRUE)[[1]], ..., echo=echo)
val$cmdline <- paste("docker-compose", cmd)
invisible(val)
@ -161,7 +163,7 @@ call_docker_compose <- function(cmd="", ..., echo=TRUE)
#'
#' }
#' @export
call_kubectl <- function(cmd="", config=NULL, ..., echo=TRUE)
call_kubectl <- function(cmd="", config=NULL, ..., echo=getOption("azure_containers_tool_echo", TRUE))
{
if(.AzureContainers$kubectl == "")
stop("kubectl binary not found", call.=FALSE)
@ -169,6 +171,8 @@ call_kubectl <- function(cmd="", config=NULL, ..., echo=TRUE)
if(!is.null(config))
config <- paste0("--kubeconfig=", config)
message("Kubernetes operation: ", cmd, " ", config)
echo <- as.logical(echo)
val <- processx::run(.AzureContainers$kubectl, c(strsplit(cmd, " ", fixed=TRUE)[[1]], config), ..., echo=echo)
val$cmdline <- paste("kubectl", cmd, config)
invisible(val)
@ -203,7 +207,7 @@ call_kubectl <- function(cmd="", config=NULL, ..., echo=TRUE)
#' [Kubectl command line reference](https://kubernetes.io/docs/reference/kubectl/overview/)
#'
#' @export
call_helm <- function(cmd="", config=NULL, ..., echo=TRUE)
call_helm <- function(cmd="", config=NULL, ..., echo=getOption("azure_containers_tool_echo", TRUE))
{
if(.AzureContainers$helm == "")
stop("helm binary not found", call.=FALSE)
@ -211,6 +215,8 @@ call_helm <- function(cmd="", config=NULL, ..., echo=TRUE)
if(!is.null(config))
config <- paste0("--kubeconfig=", config)
message("Helm operation: ", cmd, " ", config)
echo <- as.logical(echo)
val <- processx::run(.AzureContainers$helm, c(strsplit(cmd, " ", fixed=TRUE)[[1]], config), ..., echo=echo)
val$cmdline <- paste("helm", cmd, config)
invisible(val)

Просмотреть файл

@ -38,7 +38,7 @@ reg$push("newcontainer")
# create Kubernetes cluster with 2 nodes
aks <- resgroup$create_aks("myakscluster",
location="australiaeast",
agent_pools=aks_pools("pool1", 2, "Standard_DS2_v2", "Linux"))
agent_pools=agent_pool("pool1", 2))
# give the cluster pull access to the registry
acr$add_role_assignment(aks, "Acrpull")

Просмотреть файл

@ -4,10 +4,6 @@
\name{DockerRegistry}
\alias{DockerRegistry}
\title{Docker registry class}
\format{An object of class \code{R6ClassGenerator} of length 24.}
\usage{
DockerRegistry
}
\description{
Class representing a \href{https://docs.docker.com/registry/}{Docker registry}. Note that this class can be used to interface with any Docker registry that supports the HTTP V2 API, not just those created via the Azure Container Registry service. Use the \link{docker_registry} function to instantiate new objects of this class.
}
@ -15,9 +11,9 @@ Class representing a \href{https://docs.docker.com/registry/}{Docker registry}.
The following methods are available, in addition to those provided by the \link[AzureRMR:az_resource]{AzureRMR::az_resource} class:
\itemize{
\item \code{login(...)}: Do a local login to the registry via \code{docker login}; necessary if you want to push and pull images. By default, instantiating a new object of this class will also log you in. See 'Details' below.
\item \code{push(src_image, dest_image, ...)}: Push an image to the registry, using \code{docker tag} and \code{docker push}.
\item \code{pull(image, ...)}: Pull an image from the registry, using \code{docker pull}.
\item \code{login(...)}: Do a local login to the registry via \verb{docker login}; necessary if you want to push and pull images. By default, instantiating a new object of this class will also log you in. See 'Details' below.
\item \code{push(src_image, dest_image, ...)}: Push an image to the registry, using \verb{docker tag} and \verb{docker push}.
\item \code{pull(image, ...)}: Pull an image from the registry, using \verb{docker pull}.
\item \code{get_image_manifest(image, tag="latest")}: Gets the manifest for an image.
\item \code{get_image_digest(image, tag="latest")}: Gets the digest (SHA hash) for an image.
\item \code{delete_image(image, digest, confirm=TRUE)}: Deletes an image from the registry.
@ -65,4 +61,3 @@ reg$get_image_digest("myimage")
\href{https://docs.docker.com/registry/spec/api/}{Docker registry API}
}
\keyword{datasets}

Просмотреть файл

@ -4,10 +4,6 @@
\name{KubernetesCluster}
\alias{KubernetesCluster}
\title{Kubernetes cluster class}
\format{An object of class \code{R6ClassGenerator} of length 24.}
\usage{
KubernetesCluster
}
\description{
Class representing a \href{https://kubernetes.io/docs/home/}{Kubernetes} cluster. Note that this class can be used to interface with any Docker registry that supports the HTTP V2 API, not just those created via the Azure Container Registry service. Use the \link{kubernetes_cluster} function to instantiate new objects of this class.
}
@ -18,12 +14,12 @@ The following methods are available, in addition to those provided by the \link[
\item \code{new(...)}: Initialize a new registry object. See 'Initialization' below.
\item \code{create_registry_secret(registry, secret_name, email)}: Provide authentication secret for a Docker registry. See 'Secrets' below.
\item \code{delete_registry_secret(secret_name)}: Delete a registry authentication secret.
\item \code{create(file, ...)}: Creates a deployment or service from a file, using \code{kubectl create -f}.
\item \code{get(type, ...)}: Get information about resources, using \code{kubectl get}.
\item \code{run(name, image, ...)}: Run an image using \code{kubectl run --image}.
\item \code{expose(name, type, file, ...)}: Expose a service using \code{kubectl expose}. If the \code{file} argument is provided, read service information from there.
\item \code{delete(type, name, file, ...)}: Delete a resource (deployment or service) using \code{kubectl delete}. If the \code{file} argument is provided, read resource information from there.
\item \code{apply(file, ...)}: Apply a configuration file, using \code{kubectl apply -f}.
\item \code{create(file, ...)}: Creates a deployment or service from a file, using \verb{kubectl create -f}.
\item \code{get(type, ...)}: Get information about resources, using \verb{kubectl get}.
\item \code{run(name, image, ...)}: Run an image using \verb{kubectl run --image}.
\item \code{expose(name, type, file, ...)}: Expose a service using \verb{kubectl expose}. If the \code{file} argument is provided, read service information from there.
\item \code{delete(type, name, file, ...)}: Delete a resource (deployment or service) using \verb{kubectl delete}. If the \code{file} argument is provided, read resource information from there.
\item \code{apply(file, ...)}: Apply a configuration file, using \verb{kubectl apply -f}.
\item \code{show_dashboard(port, ...)}: Display the cluster dashboard. By default, use local port 30000.
\item \code{kubectl(cmd, ...)}: Run an arbitrary \code{kubectl} command on this cluster. Called by the other methods above.
\item \code{helm(cmd, ...)}: Run a \code{helm} command on this cluster.
@ -32,7 +28,7 @@ The following methods are available, in addition to those provided by the \link[
\section{Initialization}{
The \code{new()} method takes one argument: \code{config}, the name of the file containing the configuration details for the cluster. This should be a YAML or JSON file in the standard Kubernetes configuration format. Set this to NULL to use the default \code{~/.kube/config} file.
The \code{new()} method takes one argument: \code{config}, the name of the file containing the configuration details for the cluster. This should be a YAML or JSON file in the standard Kubernetes configuration format. Set this to NULL to use the default \verb{~/.kube/config} file.
}
\section{Secrets}{
@ -122,4 +118,3 @@ kubclus$get("service")
\href{https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands}{Kubectl commandline reference}
}
\keyword{datasets}

Просмотреть файл

@ -5,10 +5,6 @@
\alias{aci}
\alias{az_container_instance}
\title{Azure Container Instance class}
\format{An object of class \code{R6ClassGenerator} of length 24.}
\usage{
aci
}
\description{
Class representing an Azure Container Instance (ACI) resource.
}
@ -49,4 +45,3 @@ myaci$restart()
\href{https://docs.docker.com/engine/reference/commandline/cli/}{Docker commandline reference}
}
\keyword{datasets}

Просмотреть файл

@ -5,10 +5,6 @@
\alias{acr}
\alias{az_container_registry}
\title{Azure Container Registry class}
\format{An object of class \code{R6ClassGenerator} of length 24.}
\usage{
acr
}
\description{
Class representing an Azure Container Registry (ACR) resource. For working with the registry endpoint itself, including uploading and downloading images etc, see \link{docker_registry}.
}
@ -75,4 +71,3 @@ myacr$get_docker_registry(as_admin=TRUE)
\href{https://docs.microsoft.com/en-us/azure/container-registry/}{Azure Container Registry} and
\href{https://docs.microsoft.com/en-us/rest/api/containerregistry/registries}{API reference}
}
\keyword{datasets}

57
man/agent_pool.Rd Normal file
Просмотреть файл

@ -0,0 +1,57 @@
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/az_kubernetes_service.R
\name{agent_pool}
\alias{agent_pool}
\title{Utility function for specifying Kubernetes agent pools}
\usage{
agent_pool(name, count, size = "Standard_DS2_v2", os = "Linux",
disksize = 0, use_scaleset = TRUE, low_priority = FALSE,
cluster_autoscale = FALSE, ...)
}
\arguments{
\item{name}{The name(s) of the pool(s).}
\item{count}{The number of nodes per pool.}
\item{size}{The VM type (size) to use for the pool. To see a list of available VM sizes, use the \link{list_vm_sizes} method for the resource group or subscription classes.}
\item{os}{The operating system to use for the pool. Can be "Linux" or "Windows".}
\item{disksize}{The OS disk size in gigabytes for each node in the pool. A value of 0 means to use the default disk size for the VM type.}
\item{use_scaleset}{Whether to use a VM scaleset instead of individual VMs for this pool. A scaleset offers greater flexibility than individual VMs, and is the recommended method of creating an agent pool.}
\item{low_priority}{If this pool uses a scaleset, whether it should be made up of spot (low-priority) VMs. A spot VM pool is cheaper, but is subject to being evicted to make room for other, higher-priority workloads. Ignored if \code{use_scaleset=FALSE}.}
\item{cluster_autoscale}{The autoscaling parameters for the pool. This can be either \code{FALSE}, meaning autoscaling is disabled, or a vector of 2 numbers giving the minimum and maximum size of the agent pool. Ignored if \code{use_scaleset=FALSE}.}
\item{...}{Other named arguments, to be used as parameters for the agent pool.}
}
\value{
An object of class \code{agent_pool}, suitable for passing to the \code{create_aks} constructor method.
}
\description{
Utility function for specifying Kubernetes agent pools
}
\details{
\code{agent_pool} is a convenience function to simplify the task of specifying the agent pool for a Kubernetes cluster.
}
\examples{
# pool of 5 Linux GPU-enabled VMs
agent_pool("pool1", 5, size="Standard_NC6s_v3")
# pool of 3 Windows Server VMs, 500GB disk size each
agent_pool("pool1", 3, os="Windows", disksize=500)
# enable cluster autoscaling, with a minimum of 1 and maximum of 10 nodes
agent_pool("pool1", 5, cluster_autoscale=c(1, 10))
# use individual VMs rather than scaleset
agent_pool("vmpool1", 3, use_scaleset=FALSE)
}
\seealso{
\link{create_aks}, \link{list_vm_sizes}
\href{https://docs.microsoft.com/en-us/rest/api/aks/managedclusters/createorupdate#managedclusteragentpoolprofile}{Agent pool parameters on Microsoft Docs}
}

Просмотреть файл

@ -5,10 +5,6 @@
\alias{aks}
\alias{az_kubernetes_service}
\title{Azure Kubernetes Service class}
\format{An object of class \code{R6ClassGenerator} of length 24.}
\usage{
aks
}
\description{
Class representing an Azure Kubernetes Service (AKS) resource. For working with the cluster endpoint itself, including deploying images, creating services etc, see \link{kubernetes_cluster}.
}
@ -18,6 +14,10 @@ The following methods are available, in addition to those provided by the \link[
\itemize{
\item \code{new(...)}: Initialize a new AKS object.
\item \code{get_cluster(config, role)}: Return an object representing the Docker registry endpoint.
\item \code{get_agent_pool(pollname)}: Returns an object of class \code{az_agent_pool} representing an agent pool. This class inherits from \link[AzureRMR:az_resource]{AzureRMR::az_resource}; it currently has no extra methods, but these may be added in the future.
\item \code{list_agent_pools()}: Returns a list of agent pool objects.
\item \code{create_agent_pool(poolname, ..., wait=FALSE)}: Creates a new agent pool. See the \link{agent_pool} function for further arguments to this method.
\item \verb{delete_agent_pool(poolname, confirm=TRUE. wait=FALSE)}: Deletes an agent pool.
\item \code{list_cluster_resources()}: Returns a list of all the Azure resources managed by the cluster.
\item \code{update_aad_password(name=NULL, duration=NULL, ...)}: Update the password for Azure Active Directory integration, returning the new password invisibly. See 'Updating credentials' below.
\item \code{update_service_password(name=NULL, duration=NULL, ...)}: Update the password for the service principal used to manage the cluster resources, returning the new password invisibly. See 'Updating credentials' below.
@ -32,16 +32,16 @@ Note that this class is separate from the Kubernetes cluster itself. This class
For working with the cluster, including deploying images, services, etc use the object generated with the \code{get_cluster} method. This method takes two optional arguments:
\itemize{
\item \code{config}: The file in which to store the cluster configuration details. By default, this will be located in the AzureR configuration directory if it exists (see \link[AzureAuth:AzureR_dir]{AzureAuth::AzureR_dir}); otherwise, in the R temporary directory. To use the Kubernetes default \code{~/.kube/config} file, set this argument to NULL. Any existing file in the given location will be overwritten.
\item \code{config}: The file in which to store the cluster configuration details. By default, this will be located in the AzureR configuration directory if it exists (see \link[AzureAuth:AzureR_dir]{AzureAuth::AzureR_dir}); otherwise, in the R temporary directory. To use the Kubernetes default \verb{~/.kube/config} file, set this argument to NULL. Any existing file in the given location will be overwritten.
\item \code{role}: This can be \code{"User"} (the default) or \code{"Admin"}.
}
}
\section{Updating credentials}{
An AKS cluster requires at least one, and possibly three, service principals. The first service principal is used to manage the resources used by the cluster: the VMs, networking resources, virtual disks, etc. The other two are used for AAD integration. These service principals have secret passwords, which have to be refreshed as they expire.
An AKS resource can have up to three service principals associated with it. Two of these are for Azure Active Directory (AAD) integration. The third is used to manage the subsidiary resources (VMs, networks, disks, etc) used by the cluster, if it doesn't have a service identity.
The \code{update_aad_password()} and \code{update_service_password()} methods let you refresh the passwords for the cluster's service principals. Their arguments are:
Any service principals used by the AKS resource will have secret passwords, which have to be refreshed as they expire. The \code{update_aad_password()} and \code{update_service_password()} methods let you refresh the passwords for the cluster's service principals. Their arguments are:
\itemize{
\item \code{name}: An optional friendly name for the password.
\item \code{duration}: The duration for which the new password is valid. Defaults to 2 years.
@ -64,7 +64,14 @@ myaks$sync_fields()
# get the cluster endpoint
kubclus <- myaks$get_cluster()
# refresh the service principal password
# list of agent pools
myaks$list_agent_pools()
# create a new agent pool, then delete it
pool <- myaks$create_agent_pool("pool2", 3, size="Standard_DS3_v2")
pool$delete()
# refresh the service principal password (mostly for legacy clusters without a managed identity)
myaks$update_service_password()
# refresh the service principal password, using custom credentials to authenticate with MS Graph
@ -81,4 +88,3 @@ myaks$update_service_password(app="app_id", password="app_password")
\href{https://docs.microsoft.com/en-us/azure/aks/}{AKS documentation} and
\href{https://docs.microsoft.com/en-us/rest/api/aks/}{API reference}
}
\keyword{datasets}

Просмотреть файл

@ -2,7 +2,7 @@
% Please edit documentation in R/az_kubernetes_service.R
\name{aks_pools}
\alias{aks_pools}
\title{Utility function for specifying Kubernetes agent pools}
\title{Vectorised utility function for specifying Kubernetes agent pools}
\usage{
aks_pools(name, count, size = "Standard_DS2_v2", os = "Linux")
}
@ -19,10 +19,12 @@ aks_pools(name, count, size = "Standard_DS2_v2", os = "Linux")
A list of lists, suitable for passing to the \code{create_aks} constructor method.
}
\description{
Utility function for specifying Kubernetes agent pools
Vectorised utility function for specifying Kubernetes agent pools
}
\details{
This is a convenience function to simplify the task of specifying the agent pool for a Kubernetes cluster. You can specify multiple pools by providing vectors as input arguments; any scalar inputs will be replicated to match.
\code{aks_pools} is deprecated; please use \link{agent_pool} going forward.
}
\examples{
# 1 pool of 5 Linux VMs
@ -36,5 +38,5 @@ aks_pools(c("pool1", "pool2"), count=c(3, 3), size=c("Standard_DS2_v2", "Standar
}
\seealso{
\link{list_vm_sizes}
\link{list_vm_sizes}, \link{agent_pool}
}

Просмотреть файл

@ -4,7 +4,8 @@
\alias{call_docker}
\title{Call the docker commandline tool}
\usage{
call_docker(cmd = "", ..., echo = TRUE)
call_docker(cmd = "", ..., echo = getOption("azure_containers_tool_echo",
TRUE))
}
\arguments{
\item{cmd}{The docker command line to execute.}

Просмотреть файл

@ -4,7 +4,8 @@
\alias{call_docker_compose}
\title{Call the docker-compose commandline tool}
\usage{
call_docker_compose(cmd = "", ..., echo = TRUE)
call_docker_compose(cmd = "", ...,
echo = getOption("azure_containers_tool_echo", TRUE))
}
\arguments{
\item{cmd}{The docker-compose command line to execute.}

Просмотреть файл

@ -4,7 +4,8 @@
\alias{call_helm}
\title{Call the Helm commandline tool}
\usage{
call_helm(cmd = "", config = NULL, ..., echo = TRUE)
call_helm(cmd = "", config = NULL, ...,
echo = getOption("azure_containers_tool_echo", TRUE))
}
\arguments{
\item{cmd}{The Helm command line to execute.}

Просмотреть файл

@ -4,7 +4,8 @@
\alias{call_kubectl}
\title{Call the Kubernetes commandline tool, kubectl}
\usage{
call_kubectl(cmd = "", config = NULL, ..., echo = TRUE)
call_kubectl(cmd = "", config = NULL, ...,
echo = getOption("azure_containers_tool_echo", TRUE))
}
\arguments{
\item{cmd}{The kubectl command line to execute.}

Просмотреть файл

@ -17,7 +17,7 @@ Method for the \link[AzureRMR:az_resource_group]{AzureRMR::az_resource_group} cl
\itemize{
\item \code{name}: The name of the container registry.
\item \code{location}: The location/region in which to create the container registry. Defaults to this resource group's location.
\item \code{admin_user_enabled}: Whether to enable the Admin user. Currently this must be TRUE to allow Docker to access the registry.
\item \code{admin_user_enabled}: Whether to enable the Admin user. Currently this must be \code{TRUE} for ACI to pull from the registry.
\item \code{sku}: Either "Basic", "Standard" (the default) or "Premium".
\item \code{wait}: Whether to wait until the ACR resource provisioning is complete.
\item \code{...}: Other named arguments to pass to the \link{az_resource} initialization function.

Просмотреть файл

@ -9,9 +9,10 @@ Method for the \link[AzureRMR:az_resource_group]{AzureRMR::az_resource_group} cl
\section{Usage}{
\preformatted{create_aks(name, location = self$location,
dns_prefix = name, kubernetes_version = NULL,
enable_rbac = FALSE, agent_pools = list(),
enable_rbac = FALSE, agent_pools = agent_pool("pool1", 3),
login_user = "", login_passkey = "",
cluster_service_principal = NULL, managed_identity = FALSE,
cluster_service_principal = NULL, managed_identity = TRUE,
private_cluster = FALSE,
properties = list(), ..., wait = TRUE)
}
}
@ -23,11 +24,12 @@ Method for the \link[AzureRMR:az_resource_group]{AzureRMR::az_resource_group} cl
\item \code{location}: The location/region in which to create the service. Defaults to this resource group's location.
\item \code{dns_prefix}: The domain name prefix to use for the cluster endpoint. The actual domain name will start with this argument, followed by a string of pseudorandom characters.
\item \code{kubernetes_version}: The Kubernetes version to use. If not specified, uses the most recent version of Kubernetes available.
\item \code{enable_rbac}: Whether to enable role-based access controls.
\item \code{agent_pools}: A list of pool specifications. See 'Details'.
\item \code{login_user,login_passkey}: Optionally, a login username and public key (on Linux). Specify these if you want to be able to ssh into the cluster nodes.
\item \code{cluster_service_principal}: The service principal (client) that AKS will use to manage the cluster resources. This should be a list, with the first component being the client ID and the second the client secret. If not supplied, a new service principal will be created (requires an interactive session).
\item \code{managed_identity}: Whether the cluster should have a managed identity assigned to it. This is currently in preview; see the \href{https://docs.microsoft.com/en-us/azure/aks/use-managed-identity}{Microsoft Docs page} for enabling this feature.
\item \code{enable_rbac}: Whether to enable Kubernetes role-based access controls (which is distinct from Azure AD RBAC).
\item \code{agent_pools}: The pool specification(s) for the cluster. See 'Details'.
\item \verb{login_user,login_passkey}: Optionally, a login username and public key (on Linux). Specify these if you want to be able to ssh into the cluster nodes.
\item \code{cluster_service_principal}: The service principal that AKS will use to manage the cluster resources. This should be a list, with the first component being the client ID and the second the client secret. If not supplied, a new service principal will be created (requires an interactive session). Ignored if \code{managed_identity=TRUE}, which is the default.
\item \code{managed_identity}: Whether the cluster should have a managed identity assigned to it. If \code{FALSE}, a service principal will be used to manage the cluster's resources; see 'Details' below.
\item \code{private_cluster}: Whether this cluster is private (not visible from the public Internet). A private cluster is accessible only to hosts on its virtual network.
\item \code{properties}: A named list of further Kubernetes-specific properties to pass to the initialization function.
\item \code{wait}: Whether to wait until the AKS resource provisioning is complete. Note that provisioning a Kubernetes cluster can take several minutes.
\item \code{...}: Other named arguments to pass to the initialization function.
@ -38,9 +40,13 @@ Method for the \link[AzureRMR:az_resource_group]{AzureRMR::az_resource_group} cl
An AKS resource is a Kubernetes cluster hosted in Azure. See the \link[=aks]{documentation for the resource} for more information. To work with the cluster (deploy images, define and start services, etc) see the \link[=kubernetes_cluster]{documentation for the cluster endpoint}.
To specify the agent pools for the cluster, it is easiest to use the \link{aks_pools} function. This takes as arguments the name(s) of the pools, the number of nodes, the VM size(s) to use, and the operating system (Windows or Linux) to run on the VMs.
The nodes for an AKS cluster are organised into \emph{agent pools}, also known as \emph{node pools}, which are homogenous groups of virtual machines. To specify the details for a single agent pool, use the \code{agent_pool} function, which returns an S3 object of that class. To specify the details for multiple pools, you can supply a list of such objects, or a single call to the \code{aks_pools} function; see the examples below. Note that \code{aks_pools} is older, and does not support all the possible parameters for an agent pool.
By default, the password for a newly-created service principal will expire after one year. You can run the \code{update_service_password} method of the AKS object to reset/update the password before it expires.
Of the agent pools in a cluster, at least one must be a \emph{system pool}, which is used to host critical system pods such as CoreDNS and tunnelfront. If you specify more than one pool, the first pool will be treated as the system pool. Note that there are certain \href{https://docs.microsoft.com/en-us/azure/aks/use-system-pools}{extra requirements} for the system pool.
An AKS cluster requires an identity to manage the low-level resources it uses, such as virtual machines and networks. The default and recommended method is to use a \emph{managed identity}, in which all the details of this process are handled by AKS. In AzureContainers version 1.2.1 and older, a \emph{service principal} was used instead, which is an older and less automated method. By setting \code{managed_identity=FALSE}, you can continue using a service principal instead of a managed identity.
One thing to be aware of with service principals is that they have a secret password that will expire eventually. By default, the password for a newly-created service principal will expire after one year. You should run the \code{update_service_password} method of the AKS object to reset/update the password before it expires.
}
\section{Value}{
@ -55,15 +61,25 @@ rg <- AzureRMR::get_azure_login()$
get_subscription("subscription_id")$
get_resource_group("rgname")
rg$create_aks("mycluster", agent_pools=aks_pools("pool1", 5))
rg$create_aks("mycluster", agent_pools=agent_pool("pool1", 5))
# GPU-enabled cluster
rg$create_aks("mygpucluster", agent_pools=aks_pools("pool1", 5, size="Standard_NC6s_v3"))
rg$create_aks("mygpucluster", agent_pools=agent_pool("pool1", 5, size="Standard_NC6s_v3"))
# multiple agent pools
rg$create_aks("mycluster", agent_pools=list(
agent_pool("pool1", 2),
agent_pool("pool2", 3, size="Standard_NC6s_v3")
))
# deprecated alternative for multiple pools
rg$create_aks("mycluster",
agent_pools=aks_pools(c("pool1", "pool2"), c(2, 3), c("Standard_DS2_v2", "Standard_NC6s_v3")))
}
}
\seealso{
\link{get_aks}, \link{delete_aks}, \link{list_aks}, \link{aks_pools}
\link{get_aks}, \link{delete_aks}, \link{list_aks}, \link{agent_pool}, \link{aks_pools}
\link{az_kubernetes_service}

Просмотреть файл

@ -7,7 +7,7 @@
kubernetes_cluster(config = NULL)
}
\arguments{
\item{config}{The name of the file containing the configuration details for the cluster. This should be a YAML or JSON file in the standard Kubernetes configuration format. Set this to NULL to use the default \code{~/.kube/config} file.}
\item{config}{The name of the file containing the configuration details for the cluster. This should be a YAML or JSON file in the standard Kubernetes configuration format. Set this to NULL to use the default \verb{~/.kube/config} file.}
}
\value{
An R6 object of class \code{KubernetesCluster}.

Просмотреть файл

@ -1,13 +1,16 @@
apiVersion: extensions/v1beta1
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello
name: hellodep
spec:
selector:
matchLabels:
app: hellodep
replicas: 1
template:
metadata:
labels:
app: hello
app: hellodep
spec:
containers:
- name: acrname
@ -18,10 +21,10 @@ spec:
apiVersion: v1
kind: Service
metadata:
name: hello-svc
name: hellodep-svc
spec:
selector:
app: hello
app: hellodep
type: LoadBalancer
ports:
- protocol: TCP

4
tests/testthat/setup.R Normal file
Просмотреть файл

@ -0,0 +1,4 @@
make_name <- function(n=20)
{
paste0(sample(letters, n, TRUE), collapse="")
}

Просмотреть файл

@ -1,26 +0,0 @@
context("Resource group creation")
tenant <- Sys.getenv("AZ_TEST_TENANT_ID")
app <- Sys.getenv("AZ_TEST_APP_ID")
password <- Sys.getenv("AZ_TEST_PASSWORD")
subscription <- Sys.getenv("AZ_TEST_SUBSCRIPTION")
if(tenant == "" || app == "" || password == "" || subscription == "")
skip("Tests skipped: ARM credentials not set")
Sys.setenv(AZ_TEST_RG=paste(sample(letters, 20, replace=TRUE), collapse=""))
Sys.setenv(AZ_TEST_ACR=paste(sample(letters, 10, replace=TRUE), collapse=""))
test_that("Resource group creation succeeds",
{
sub <- AzureRMR::az_rm$
new(tenant=tenant, app=app, password=password)$
get_subscription(subscription)
rgname <- Sys.getenv("AZ_TEST_RG")
expect_false(sub$resource_group_exists(rgname))
rg <- sub$create_resource_group(rgname, location="australiaeast")
expect_true(sub$resource_group_exists(rgname))
})

Просмотреть файл

@ -8,15 +8,16 @@ subscription <- Sys.getenv("AZ_TEST_SUBSCRIPTION")
if(tenant == "" || app == "" || password == "" || subscription == "")
skip("Tests skipped: ARM credentials not set")
acrname <- Sys.getenv("AZ_TEST_ACR")
if(acrname == "")
skip("ACR tests skipped: resource name not set")
rgname <- Sys.getenv("AZ_TEST_RG")
rgname <- make_name(10)
rg <- AzureRMR::az_rm$
new(tenant=tenant, app=app, password=password)$
get_subscription(subscription)$
get_resource_group(rgname)
create_resource_group(rgname, location="australiaeast")
echo <- getOption("azure_containers_tool_echo")
options(azure_containers_tool_echo=FALSE)
acrname <- make_name(10)
test_that("ACR works",
{
@ -67,3 +68,8 @@ test_that("ACR works with app login",
expect_equal(reg$list_repositories(), c("hello-world", "hello-world-sp"))
})
teardown({
options(azure_containers_tool_echo=echo)
suppressMessages(rg$delete(confirm=FALSE))
})

Просмотреть файл

@ -8,19 +8,33 @@ subscription <- Sys.getenv("AZ_TEST_SUBSCRIPTION")
if(tenant == "" || app == "" || password == "" || subscription == "")
skip("Tests skipped: ARM credentials not set")
acrname <- Sys.getenv("AZ_TEST_ACR")
if(acrname == "")
skip("ACI tests skipped: resource names not set")
rgname <- make_name(10)
rg <- AzureRMR::az_rm$
new(tenant=tenant, app=app, password=password)$
get_subscription(subscription)$
create_resource_group(rgname, location="australiaeast")
echo <- getOption("azure_containers_tool_echo")
options(azure_containers_tool_echo=FALSE)
test_that("ACI works",
{
rgname <- Sys.getenv("AZ_TEST_RG")
rg <- AzureRMR::az_rm$
new(tenant=tenant, app=app, password=password)$
get_subscription(subscription)$
get_resource_group(rgname)
acrname <- make_name(10)
acr <- rg$create_acr(acrname, admin_user_enabled=TRUE)
reg <- acr$get_docker_registry(as_admin=TRUE)
expect_true(is_docker_registry(reg))
expect_false(is.null(reg$username) || is.null(reg$password))
aciname <- paste0(sample(letters, 10, TRUE), collapse="")
cmdline <- "build -f ../resources/hello_dockerfile -t hello-world ."
call_docker(cmdline)
reg$push("hello-world")
cmdline <- paste0("image rm ", acrname, ".azurecr.io/hello-world")
call_docker(cmdline)
# from local image
aciname <- make_name(10)
expect_true(is_aci(rg$create_aci(aciname,
image="hello-world")))
@ -32,23 +46,25 @@ test_that("ACI works",
expect_silent(aci$start())
expect_silent(aci$restart())
acr <- rg$get_acr(acrname)
expect_true(is_acr(acr))
reg <- acr$get_docker_registry(as_admin=TRUE)
expect_true(is_docker_registry(reg))
expect_false(is.null(reg$username) || is.null(reg$password))
aciname2 <- paste0(sample(letters, 10, TRUE), collapse="")
# from Resource Manager object
aciname2 <- make_name(10)
aci2 <- rg$create_aci(aciname2,
image=paste0(reg$server$hostname, "/hello-world"),
registry_creds=reg)
expect_true(is_aci(aci2))
aciname3 <- paste0(sample(letters, 10, TRUE), collapse="")
# from Docker registry object
aciname3 <- make_name(10)
aci3 <- rg$create_aci(aciname3,
image=paste0(reg$server$hostname, "/hello-world-sp"),
image=paste0(reg$server$hostname, "/hello-world"),
registry_creds=aci_creds(reg$server$hostname, app, password))
expect_true(is_aci(aci3))
})
teardown({
options(azure_containers_tool_echo=echo)
suppressMessages(rg$delete(confirm=FALSE))
})

Просмотреть файл

@ -1,65 +0,0 @@
context("AKS interface")
tenant <- Sys.getenv("AZ_TEST_TENANT_ID")
app <- Sys.getenv("AZ_TEST_APP_ID")
password <- Sys.getenv("AZ_TEST_PASSWORD")
subscription <- Sys.getenv("AZ_TEST_SUBSCRIPTION")
if(tenant == "" || app == "" || password == "" || subscription == "")
skip("Tests skipped: ARM credentials not set")
acrname <- Sys.getenv("AZ_TEST_ACR")
if(acrname == "")
skip("AKS tests skipped: resource names not set")
rgname <- Sys.getenv("AZ_TEST_RG")
rg <- AzureRMR::az_rm$
new(tenant=tenant, app=app, password=password)$
get_subscription(subscription)$
get_resource_group(rgname)
acr <- rg$get_acr(acrname)
test_that("AKS works",
{
expect_true(is_acr(acr))
reg <- acr$get_docker_registry(as_admin=TRUE)
expect_true(is_docker_registry(reg))
expect_false(is.null(reg$username) || is.null(reg$password))
expect_is(rg$list_kubernetes_versions(), "character")
aksname <- paste0(sample(letters, 10, TRUE), collapse="")
expect_true(is_aks(rg$create_aks(aksname, agent_pools=aks_pools("pool1", 2))))
expect_true(is_aks(rg$list_aks()[[1]]))
aks <- rg$get_aks(aksname)
expect_true(is_aks(aks))
aks$update_service_password()
clus <- aks$get_cluster()
expect_true(is_kubernetes_cluster(clus))
hello_yaml <- gsub("acrname", acrname, readLines("../resources/hello.yaml"))
clus$create_registry_secret(reg, email="me@example.com")
clus$create(hello_yaml)
})
test_that("AKS works with RBAC",
{
aksname <- paste0(sample(letters, 10, TRUE), collapse="")
aks <- rg$create_aks(aksname, agent_pools=aks_pools("pool1", 2))
expect_true(is_aks(aks))
acr$add_role_assignment(aks, "Acrpull")
clus <- aks$get_cluster()
expect_true(is_kubernetes_cluster(clus))
hello_yaml <- gsub("acrname", acrname, readLines("../resources/hello.yaml"))
clus$create(hello_yaml)
})

Просмотреть файл

@ -0,0 +1,56 @@
context("AKS interface with managed identity")
tenant <- Sys.getenv("AZ_TEST_TENANT_ID")
app <- Sys.getenv("AZ_TEST_APP_ID")
password <- Sys.getenv("AZ_TEST_PASSWORD")
subscription <- Sys.getenv("AZ_TEST_SUBSCRIPTION")
if(tenant == "" || app == "" || password == "" || subscription == "")
skip("Tests skipped: ARM credentials not set")
rgname <- make_name(10)
rg <- AzureRMR::az_rm$
new(tenant=tenant, app=app, password=password)$
get_subscription(subscription)$
create_resource_group(rgname, location="australiaeast")
echo <- getOption("azure_containers_tool_echo")
options(azure_containers_tool_echo=FALSE)
test_that("AKS works with managed identity",
{
aksname <- make_name(10)
expect_is(rg$list_kubernetes_versions(), "character")
expect_true(is_aks(rg$create_aks(aksname, agent_pools=agent_pool("pool1", 1), managed_identity=TRUE)))
expect_true(is_aks(rg$list_aks()[[1]]))
aks <- rg$get_aks(aksname)
expect_true(is_aks(aks))
# no SP password with svc identity
expect_error(aks$update_service_password())
pool1 <- aks$get_agent_pool("pool1")
expect_is(pool1, "az_agent_pool")
pools <- aks$list_agent_pools()
expect_true(is.list(pools) && length(pools) == 1 && all(sapply(pools, inherits, "az_agent_pool")))
pool2 <- aks$create_agent_pool("pool2", 1, disksize=500, wait=TRUE)
expect_is(pool2, "az_agent_pool")
pools <- aks$list_agent_pools()
expect_true(is.list(pools) && length(pools) == 2 && all(sapply(pools, inherits, "az_agent_pool")))
expect_message(pool2$delete(confirm=FALSE))
clus <- aks$get_cluster()
expect_true(is_kubernetes_cluster(clus))
})
teardown({
options(azure_containers_tool_echo=echo)
suppressMessages(rg$delete(confirm=FALSE))
})

Просмотреть файл

@ -0,0 +1,78 @@
context("AKS-ACR interop with managed identity")
tenant <- Sys.getenv("AZ_TEST_TENANT_ID")
app <- Sys.getenv("AZ_TEST_APP_ID")
password <- Sys.getenv("AZ_TEST_PASSWORD")
subscription <- Sys.getenv("AZ_TEST_SUBSCRIPTION")
if(tenant == "" || app == "" || password == "" || subscription == "")
skip("Tests skipped: ARM credentials not set")
rgname <- make_name(10)
rg <- AzureRMR::az_rm$
new(tenant=tenant, app=app, password=password)$
get_subscription(subscription)$
create_resource_group(rgname, location="australiaeast")
echo <- getOption("azure_containers_tool_echo")
options(azure_containers_tool_echo=FALSE)
aksname <- make_name(10)
aks <- rg$create_aks(aksname, agent_pools=agent_pool("pool1", 1), managed_identity=TRUE)
test_that("AKS/ACR works with managed identity",
{
acrname <- make_name(10)
acr <- rg$create_acr(acrname, admin_user_enabled=TRUE)
reg <- acr$get_docker_registry(as_admin=TRUE)
expect_true(is_docker_registry(reg))
cmdline <- "build -f ../resources/hello_dockerfile -t hello-world ."
call_docker(cmdline)
reg$push("hello-world")
cmdline <- paste0("image rm ", acrname, ".azurecr.io/hello-world")
call_docker(cmdline)
expect_true(is_aks(aks))
clus <- aks$get_cluster()
expect_true(is_kubernetes_cluster(clus))
hello_yaml <- gsub("acrname", acrname, readLines("../resources/hello.yaml"))
clus$create_registry_secret(reg, email="me@example.com")
clus$create(hello_yaml)
})
test_that("AKS/ACR works with managed identity/RBAC",
{
acrname <- make_name(10)
acr <- rg$create_acr(acrname, admin_user_enabled=FALSE)
reg <- acr$get_docker_registry(as_admin=FALSE)
expect_true(is_docker_registry(reg))
cmdline <- "build -f ../resources/hello_dockerfile -t hello-world ."
call_docker(cmdline)
reg$push("hello-world")
cmdline <- paste0("image rm ", acrname, ".azurecr.io/hello-world")
call_docker(cmdline)
acr$add_role_assignment(aks, "Acrpull")
clus <- aks$get_cluster()
expect_true(is_kubernetes_cluster(clus))
hello_yaml <- gsub("acrname", acrname, readLines("../resources/hello.yaml"))
hello_yaml <- gsub("hellodep", "hellodep-rb", hello_yaml)
clus$create(hello_yaml)
})
teardown({
options(azure_containers_tool_echo=echo)
suppressMessages(rg$delete(confirm=FALSE))
})

Просмотреть файл

@ -0,0 +1,51 @@
context("AKS interface with service principal")
tenant <- Sys.getenv("AZ_TEST_TENANT_ID")
app <- Sys.getenv("AZ_TEST_APP_ID")
password <- Sys.getenv("AZ_TEST_PASSWORD")
subscription <- Sys.getenv("AZ_TEST_SUBSCRIPTION")
if(tenant == "" || app == "" || password == "" || subscription == "")
skip("Tests skipped: ARM credentials not set")
rgname <- make_name(10)
rg <- AzureRMR::az_rm$
new(tenant=tenant, app=app, password=password)$
get_subscription(subscription)$
create_resource_group(rgname, location="australiaeast")
echo <- getOption("azure_containers_tool_echo")
options(azure_containers_tool_echo=FALSE)
test_that("AKS works with service principal",
{
aksname <- paste0(sample(letters, 10, TRUE), collapse="")
expect_true(is_aks(rg$create_aks(aksname, agent_pools=agent_pool("pool1", 1), managed_identity=FALSE)))
aks <- rg$get_aks(aksname)
expect_true(is_aks(aks))
expect_message(aks$update_service_password())
pool1 <- aks$get_agent_pool("pool1")
expect_is(pool1, "az_agent_pool")
pools <- aks$list_agent_pools()
expect_true(is.list(pools) && length(pools) == 1 && all(sapply(pools, inherits, "az_agent_pool")))
pool2 <- aks$create_agent_pool("pool2", 1, disksize=500, wait=TRUE)
expect_is(pool2, "az_agent_pool")
pools <- aks$list_agent_pools()
expect_true(is.list(pools) && length(pools) == 2 && all(sapply(pools, inherits, "az_agent_pool")))
expect_message(pool2$delete(confirm=FALSE))
clus <- aks$get_cluster()
expect_true(is_kubernetes_cluster(clus))
})
teardown({
options(azure_containers_tool_echo=echo)
suppressMessages(rg$delete(confirm=FALSE))
})

Просмотреть файл

@ -0,0 +1,78 @@
context("AKS-ACR interop with service principal")
tenant <- Sys.getenv("AZ_TEST_TENANT_ID")
app <- Sys.getenv("AZ_TEST_APP_ID")
password <- Sys.getenv("AZ_TEST_PASSWORD")
subscription <- Sys.getenv("AZ_TEST_SUBSCRIPTION")
if(tenant == "" || app == "" || password == "" || subscription == "")
skip("Tests skipped: ARM credentials not set")
rgname <- make_name(10)
rg <- AzureRMR::az_rm$
new(tenant=tenant, app=app, password=password)$
get_subscription(subscription)$
create_resource_group(rgname, location="australiaeast")
echo <- getOption("azure_containers_tool_echo")
options(azure_containers_tool_echo=FALSE)
aksname <- make_name(10)
aks <- rg$create_aks(aksname, agent_pools=agent_pool("pool1", 1), managed_identity=FALSE)
test_that("AKS/ACR works with service principal",
{
acrname <- make_name(10)
acr <- rg$create_acr(acrname, admin_user_enabled=TRUE)
reg <- acr$get_docker_registry(as_admin=TRUE)
expect_true(is_docker_registry(reg))
cmdline <- "build -f ../resources/hello_dockerfile -t hello-world ."
call_docker(cmdline)
reg$push("hello-world")
cmdline <- paste0("image rm ", acrname, ".azurecr.io/hello-world")
call_docker(cmdline)
expect_true(is_aks(aks))
clus <- aks$get_cluster()
expect_true(is_kubernetes_cluster(clus))
hello_yaml <- gsub("acrname", acrname, readLines("../resources/hello.yaml"))
clus$create_registry_secret(reg, email="me@example.com")
clus$create(hello_yaml)
})
test_that("AKS/ACR works with service principal/RBAC",
{
acrname <- make_name(10)
acr <- rg$create_acr(acrname, admin_user_enabled=FALSE)
reg <- acr$get_docker_registry(as_admin=FALSE)
expect_true(is_docker_registry(reg))
cmdline <- "build -f ../resources/hello_dockerfile -t hello-world ."
call_docker(cmdline)
reg$push("hello-world")
cmdline <- paste0("image rm ", acrname, ".azurecr.io/hello-world")
call_docker(cmdline)
acr$add_role_assignment(aks, "Acrpull")
clus <- aks$get_cluster()
expect_true(is_kubernetes_cluster(clus))
hello_yaml <- gsub("acrname", acrname, readLines("../resources/hello.yaml"))
hello_yaml <- gsub("hellodep", "hellodep-rb", hello_yaml)
clus$create(hello_yaml)
})
teardown({
options(azure_containers_tool_echo=echo)
suppressMessages(rg$delete(confirm=FALSE))
})

Просмотреть файл

@ -0,0 +1,45 @@
context("AKS interface with availability set")
tenant <- Sys.getenv("AZ_TEST_TENANT_ID")
app <- Sys.getenv("AZ_TEST_APP_ID")
password <- Sys.getenv("AZ_TEST_PASSWORD")
subscription <- Sys.getenv("AZ_TEST_SUBSCRIPTION")
if(tenant == "" || app == "" || password == "" || subscription == "")
skip("Tests skipped: ARM credentials not set")
rgname <- make_name(10)
rg <- AzureRMR::az_rm$
new(tenant=tenant, app=app, password=password)$
get_subscription(subscription)$
create_resource_group(rgname, location="australiaeast")
echo <- getOption("azure_containers_tool_echo")
options(azure_containers_tool_echo=FALSE)
test_that("AKS works with availability set",
{
aksname <- make_name(10)
expect_true(is_aks(rg$create_aks(aksname, agent_pools=agent_pool("pool1", 1, use_scaleset=FALSE),
managed_identity=TRUE)))
aks <- rg$get_aks(aksname)
expect_true(is_aks(aks))
pool1 <- aks$get_agent_pool("pool1")
expect_is(pool1, "az_agent_pool")
pools <- aks$list_agent_pools()
expect_true(is.list(pools) && length(pools) == 1 && all(sapply(pools, inherits, "az_agent_pool")))
expect_error(aks$create_agent_pool("pool2", 1, disksize=500, wait=TRUE))
clus <- aks$get_cluster()
expect_true(is_kubernetes_cluster(clus))
})
teardown({
options(azure_containers_tool_echo=echo)
suppressMessages(rg$delete(confirm=FALSE))
})

Просмотреть файл

@ -0,0 +1,45 @@
context("AKS interface with managed identity/private cluster")
tenant <- Sys.getenv("AZ_TEST_TENANT_ID")
app <- Sys.getenv("AZ_TEST_APP_ID")
password <- Sys.getenv("AZ_TEST_PASSWORD")
subscription <- Sys.getenv("AZ_TEST_SUBSCRIPTION")
if(tenant == "" || app == "" || password == "" || subscription == "")
skip("Tests skipped: ARM credentials not set")
rgname <- make_name(10)
rg <- AzureRMR::az_rm$
new(tenant=tenant, app=app, password=password)$
get_subscription(subscription)$
create_resource_group(rgname, location="australiaeast")
echo <- getOption("azure_containers_tool_echo")
options(azure_containers_tool_echo=FALSE)
test_that("AKS works with private cluster",
{
aksname <- make_name(10)
expect_true(is_aks(rg$create_aks(aksname, agent_pools=agent_pool("pool1", 1),
managed_identity=TRUE, private_cluster=TRUE)))
aks <- rg$get_aks(aksname)
expect_true(is_aks(aks))
# no SP password with svc identity
expect_error(aks$update_service_password())
pool1 <- aks$get_agent_pool("pool1")
expect_is(pool1, "az_agent_pool")
pools <- aks$list_agent_pools()
expect_true(is.list(pools) && length(pools) == 1 && all(sapply(pools, inherits, "az_agent_pool")))
clus <- aks$get_cluster()
expect_true(is_kubernetes_cluster(clus))
})
teardown({
options(azure_containers_tool_echo=echo)
suppressMessages(rg$delete(confirm=FALSE))
})

Просмотреть файл

@ -1,21 +0,0 @@
context("Resource group deletion")
tenant <- Sys.getenv("AZ_TEST_TENANT_ID")
app <- Sys.getenv("AZ_TEST_APP_ID")
password <- Sys.getenv("AZ_TEST_PASSWORD")
subscription <- Sys.getenv("AZ_TEST_SUBSCRIPTION")
if(tenant == "" || app == "" || password == "" || subscription == "")
skip("Tests skipped: ARM credentials not set")
test_that("Resource group deletion succeeds",
{
sub <- AzureRMR::az_rm$
new(tenant=tenant, app=app, password=password)$
get_subscription(subscription)
rgname <- Sys.getenv("AZ_TEST_RG")
expect_true(sub$resource_group_exists(rgname))
sub$delete_resource_group(rgname, confirm=FALSE)
})

Просмотреть файл

@ -24,7 +24,7 @@ library(randomForest)
bos_rf <- randomForest(medv ~ ., data=Boston, ntree=100)
# save the model
saveRDS(bos.rf, "bos_rf.rds")
saveRDS(bos_rf, "bos_rf.rds")
```
@ -58,7 +58,6 @@ Let's package up the model and the scoring script into a Docker image. A Dockerf
# example Dockerfile to expose a plumber service
FROM trestletech/plumber
MAINTAINER Hong Ooi <hongooi@microsoft.com>
# install the randomForest package
RUN R -e 'install.packages(c("randomForest"))'
@ -118,13 +117,6 @@ deployaci <- deployresgrp$create_aci("deployaci",
ports=aci_ports(8000))
```
To check on the progress of the deployment, call the object's `sync_fields()` method. This will update and return its provisioning state, which will be `"Creating"` while the instance is being created, and `"Succeeded"` once it is running. Deploying a simple container like this usually takes less than a minute.
```r
deployaci$sync_fields()
#> [1] "Succeeded"
```
Once the instance is running, let's call the prediction API with some sample data. By default, AzureContainers will assign the container a domain name with prefix taken from the instance name. The port is 8000 as specified in the Dockerfile, and the URI path is `/score` indicating we want to call the scoring function defined earlier.
The data to be scored---the first 10 rows of the Boston dataset---is passed in the _body_ of the request as a named list, encoded as JSON. A feature of Plumber is that, when the body of the request is in this format, it will extract the elements of the list and pass them to the scoring function as named arguments. This makes it easy to pass around relatively large amounts of data, eg if the data is wide, or for scoring multiple rows at a time. For more information on how to create and interact with Plumber APIs, consult the [Plumber documentation](https://www.rplumber.io/docs/).
@ -132,7 +124,7 @@ The data to be scored---the first 10 rows of the Boston dataset---is passed in t
```r
response <- httr::POST("http://deployaci.australiaeast.azurecontainer.io:8000/score",
body=list(df=MASS::Boston[1:10,]), encode="json")
httr::content(response, flatten=TRUE)
httr::content(response, simplifyVector=TRUE)
#> [1] 25.9269 22.0636 34.1876 33.7737 34.8081 27.6394 21.8007 22.3577 16.7812 18.9785
```
@ -143,7 +135,7 @@ Deploying a service to a container instance is simple, but lacks many features t
```r
# create a Kubernetes cluster with 2 nodes, running Linux (the default)
deployclus_svc <- deployresgrp$create_aks("deployclus", agent_pools=aks_pools("pool1", 2))
deployclus_svc <- deployresgrp$create_aks("deployclus", agent_pools=agent_pool("pool1", 2))
```
Unlike an ACI resource, creating a Kubernetes cluster can take several minutes. By default, the `create_aks()` method will wait until the cluster provisioning is complete before it returns.
@ -151,11 +143,14 @@ Unlike an ACI resource, creating a Kubernetes cluster can take several minutes.
Having created the cluster, we can deploy our model and create a service. We'll use a YAML configuration file to specify the details for the deployment and service API. The image to be deployed is the same as before.
```yaml
apiVersion: extensions/v1beta1
apiVersion: apps/v1
kind: Deployment
metadata:
name: bos-rf
spec:
selector:
matchLabels:
app: bos-rf
replicas: 1
template:
metadata:
@ -192,9 +187,7 @@ The following code will obtain the cluster endpoint from the AKS resource and th
```r
# grant the cluster pull access to the registry
gr <- AzureGraph::get_graph_login()
aks_app_id <- deployclus$properties$servicePrincipalProfile$clientID
reg$add_role_assignment(gr$get_app(aks_app_id), "Acrpull")
deployreg_svc$add_role_assignment(deployclus_svc, "Acrpull")
# get the cluster endpoint
deployclus <- deployclus_svc$get_cluster()
@ -211,22 +204,22 @@ deployclus$get("deployment bos-rf")
#> NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
#> bos-rf 1 1 1 1 5m
deployclus$get("service bos-rf-svc")
svc <- read.table(text=deployclus$get("service bos-rf-svc")$stdout, header=TRUE)
#> Kubernetes operation: get service bos-rf-svc --kubeconfig=".../kubeconfigxxxx"
#> NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
#> bos-rf-svc LoadBalancer 10.0.8.189 52.187.249.58 8000:32276/TCP 5m
```
Once the service is up and running, as indicated by the presence of an external IP in the service details, let's test it with a HTTP request. The response should be the same as it was with the container instance.
Once the service is up and running, as indicated by the presence of an external IP in the service details, let's test it with a HTTP request. The response should be the same as it was with the container instance. Notice how we extract the IP address from the service details above.
```r
response <- httr::POST("http://52.187.249.58:8000/score",
response <- httr::POST(paste0("http://", svc$EXTERNAL.IP[1], ":8000/score"),
body=list(df=MASS::Boston[1:10,]), encode="json")
httr::content(response, simplifyVector=TRUE)
#> [1] 25.9269 22.0636 34.1876 33.7737 34.8081 27.6394 21.8007 22.3577 16.7812 18.9785
```
Finally, once we are done, we can tear down the service and deployment:
Finally, once we are done, we can tear down the service and deployment. Depending on the version of Kubernetes the cluster is running, deleting the service may take a few minutes.
```r
deployclus$delete("service", "bos-rf-svc")
@ -239,6 +232,10 @@ And if required, we can also delete all the resources created here, by simply de
deployresgrp$delete()
```
### Security note
One important thing to note about the above example is that it is **insecure**. The Plumber service is exposed over HTTP, and there is no authentication layer: anyone on the Internet can contact the service and interact with it. Therefore, it's highly recommended that you should provide at least some level of authentication, as well as restricting the service to HTTPS only. You can also create the AKS resource as a private cluster; however, be aware that if you do this, you can only interact with the cluster endpoint from a host which is on the cluster's own subnet.
## See also