Jekyll2023-12-12T10:18:15+00:00https://www.lightenna.com/feed.xmlLightennaConsulting and training servicesAlex StanhopeUse NextAuth (Auth.js) for both anonymous and GitHub-authenticated logins2023-10-02T00:00:00+00:002023-10-02T00:00:00+00:00https://www.lightenna.com/tech/2023/use-nextauth-with-nextjs-app-router-for-anonymous-logins<h2 id="repo">Repo</h2>
<p>All the code in this article is available <a href="https://github.com/lightenna/nextjs-app-auth-anon-logins-example">open-source in our GitHub repo</a>.</p>
<h2 id="introduction">Introduction</h2>
<p>This is an example of using next.js, nextauth.js and typescript for both anonymous sessions and authenticated sessions. It’s not a primer on how to use Next.js or Nextauth.js, but the focus instead is how to resolve a specific problem.</p>
<p>The basis of this demo is a simple OAuth login using Github as a provider. It’s from an app that allows users to <a href="https://www.notegit.com/">update notes stored in GitHub</a>. Users can’t do that without a GitHub user. However, I wanted to enable users to test the app before logging in. To do that we need to manage information before having an authenticate user account.</p>
<p>To achieve that anonymous user experience we need a session, as the user hasn’t logged in yet. This demo creates an anonymous session using a second <code class="language-plaintext highlighter-rouge">provider</code>. It’s not polished. It simply shows the session information, specifically how:</p>
<ul>
<li>It starts with an empty state (no data)</li>
<li>It’s replaced by an anonymous session automatically, typically within a few seconds</li>
<li>When the user clicks <code class="language-plaintext highlighter-rouge">Sign in</code>, they’re redirected to GitHub</li>
<li>After they’re redirected back, selected GitHub user details are visible in the session</li>
<li>When the user clicks <code class="language-plaintext highlighter-rouge">Sign out</code>, the session is wiped (no data).</li>
<li>Again, within a few seconds, a new anonymous session is created and displayed.</li>
</ul>
<h2 id="getting-started">Getting started</h2>
<ul>
<li>First install the repo
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npm install
npm run gen-secret
</code></pre></div> </div>
</li>
<li>Set up <code class="language-plaintext highlighter-rouge">.env.local</code> using placeholders from <code class="language-plaintext highlighter-rouge">.env.local.template</code>
<ul>
<li>Include the <code class="language-plaintext highlighter-rouge">NEXTAUTH_SECRET</code> generated above</li>
<li>Transpose credentials from your (already created) GitHub App</li>
</ul>
</li>
<li>Run locally
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npm run dev
</code></pre></div> </div>
</li>
<li>Open a browser to http://localhost:3000</li>
<li>Click <code class="language-plaintext highlighter-rouge">Sign in</code> button
<ul>
<li>View logs to see <code class="language-plaintext highlighter-rouge">GitHub signIn</code> event</li>
</ul>
</li>
<li>Click <code class="language-plaintext highlighter-rouge">Sign out</code> button
<ul>
<li>View logs to see <code class="language-plaintext highlighter-rouge">GitHub signOut</code> and <code class="language-plaintext highlighter-rouge">Anonymous signIn</code> events</li>
</ul>
</li>
</ul>
<h2 id="highlights">Highlights</h2>
<p>The <code class="language-plaintext highlighter-rouge">/editor</code> route is wrapped in a NextAuthProvider:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><NextAuthProvider>
<section>
<nav>
<Account/>
</nav>
{children}
</section>
</NextAuthProvider>
</code></pre></div></div>
<p>That auth provider is a session provider, but contains an <code class="language-plaintext highlighter-rouge">AnonymousSessionProvider</code> that kicks in when there’s no session.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><SessionProvider>
<AnonymousSessionProvider>
{children}
</AnonymousSessionProvider>
</SessionProvider>
</code></pre></div></div>
<p>In <code class="language-plaintext highlighter-rouge">AnonymousSessionProvider</code> one hook pulls the session information, then a second does an anonymous ‘sign-in’ if there’s no session.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> const {data: session, status} = useSession();
useEffect(() => {
if (status === "unauthenticated") {
// login as anonymous
signIn("credentials")
.then((data) => {});
}
}, [status]);
</code></pre></div></div>
<p>In the back-end, the <code class="language-plaintext highlighter-rouge">/api/auth/[...nextauth]/route</code> has two providers:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>export const authOptions: AuthOptions = {
providers: [
CredentialsProvider({
name: "anonymous",
credentials: {},
async authorize(credentials, req) {
return createAnonymousUser();
},
}),
GithubProvider({
clientId: process.env.GITHUB_CLIENT_ID as string,
clientSecret: process.env.GITHUB_CLIENT_SECRET as string,
}),
],
callbacks: {
async jwt({token, account, profile}: {token: JWT, account: Account | null, profile?: Profile}): Promise<JWT> {
if (account && account?.expires_at && account?.type === 'oauth') {
// at sign-in, persist in the JWT the GitHub account details to enable brokered requests in the future
token.access_token = account.access_token;
token.expires_at = account.expires_at;
token.refresh_token = account.refresh_token;
token.refresh_token_expires_in = account.refresh_token_expires_in;
token.provider = 'github';
}
if (!token.provider) token.provider = 'anonymous';
return token;
},
async session({session, token, user}: {session: Session, token: JWT, user: AdapterUser}): Promise<Session> {
// don't make the token (JWT) contents available to the client session (JWT), but flag that they're server-side
if (token.provider) {
session.token_provider = token.provider;
}
return session;
},
},
events: {
async signIn({user, account, profile}: {user: User, account: Account | null, profile?: Profile}): Promise<void> {
debug(`signIn of ${user.name} from ${user?.provider || account?.provider}`);
},
async signOut({session, token}: {session: Session, token: JWT}): Promise<void> {
debug(`signOut of ${token.name} from ${token.provider}`);
},
},
session: {
// use default, an encrypted JWT (JWE) store in the session cookie
strategy: "jwt" as SessionStrategy,
},
}
const handler = NextAuth(authOptions);
</code></pre></div></div>
<p>and a helper function for creating a nice anonymous user:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>const createAnonymousUser = (): User => {
// generate a random name and email for this anonymous user
const customConfig: Config = {
dictionaries: [adjectives, colors, animals],
separator: '-',
length: 3,
style: 'capital'
};
// handle is simple-red-aardvark
const unique_handle: string = uniqueNamesGenerator(customConfig).replaceAll(' ','');
// real name is Red Aardvark
const unique_realname: string = unique_handle.split('-').slice(1).join(' ');
const unique_uuid: string = randomUUID();
return {
id: unique_uuid,
email: `${unique_handle.toLowerCase()}@example.com`,
name: unique_realname,
image: "",
provider: "anonymous"
};
};
</code></pre></div></div>
<p>This took a while to puzzle out from various blog posts and GitHub issues (<a href="https://github.com/nextauthjs/next-auth/issues/568">1</a>, <a href="https://github.com/nextauthjs/next-auth/issues/6649">2</a>). I hope that it helps anyone trying to do the same thing. Please start with <a href="https://github.com/lightenna/nextjs-app-auth-anon-logins-example">the GitHub repo</a>, because the highlights shown in this blog post are illustrative only and not comprehensive, whereas the repo is complete and works at the time of writing.</p>Alex StanhopeRepo All the code in this article is available open-source in our GitHub repo.Positive Futures for AI - 2. Train2023-08-24T00:00:00+00:002023-08-24T00:00:00+00:00https://www.lightenna.com/tech/2023/positive-futures-ai-2-train<!-- Courtesy of embedresponsively.com //-->
<div class="responsive-video-container">
<iframe src="https://www.youtube-nocookie.com/embed/RSO42B1J3HU" frameborder="0" allowfullscreen=""></iframe>
</div>
<hr />Alex StanhopePositive Futures for AI - 1. Understand it2023-07-21T00:00:00+00:002023-07-21T00:00:00+00:00https://www.lightenna.com/tech/2023/positive-futures-ai-1-understand-it<!-- Courtesy of embedresponsively.com //-->
<div class="responsive-video-container">
<iframe src="https://www.youtube-nocookie.com/embed/bXW119GGJy0" frameborder="0" allowfullscreen=""></iframe>
</div>
<h3 id="introduction">Introduction</h3>
<ul>
<li>Jamais Cascio BANI framework
<ul>
<li>Applies to AI</li>
</ul>
</li>
<li>AI-nxiety
<ul>
<li>Job
<ul>
<li>Career</li>
<li>Children</li>
<li>Humankind</li>
</ul>
</li>
</ul>
</li>
<li>Series: Positive Futures
<ul>
<li>Why I shouldn’t be afraid of it
<ul>
<li>What I need to do to adapt to it</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="portrayal">Portrayal</h3>
<ul>
<li>AI is often portrayed on screen as scary</li>
<li>James Cameron’s 1984 “The Terminator”</li>
<li>Paul Verhoeven’s 1987 “Robocop”</li>
</ul>
<p><img src="https://www.lightenna.com/assets/images/term_5331463993_472f9f462d_o_640x.jpg" alt="The Terminator" />
Source: <a href="https://www.flickr.com/photos/31112252@N00/5331463993">Gaudencio Garcinuño, Terminator</a></p>
<p><img src="https://www.lightenna.com/assets/images/ed209_3976055935_c9cbe1cbaa_o_640x.jpg" alt="ED-209" />
Source: <a href="https://www.flickr.com/photos/87704691@N00/3976055935">Sam Beckwith, One ED-209 and His Dog</a></p>
<h3 id="1-understand-it">1. Understand it</h3>
<ul>
<li>AI as a function</li>
<li>Input parameters mapped to output</li>
<li>Parameters
<ul>
<li>More parameters</li>
<li>Lots more parameters</li>
</ul>
</li>
<li>Models</li>
</ul>
<h3 id="100000000000000">100,000,000,000,000</h3>
<p>100 trillion parameters in ChatGPT 4</p>
<h3 id="models-more-next-time">Models (more next time)</h3>
<ul>
<li>Statistical
<ul>
<li>Linear and logistic regression</li>
<li>Naïve Bayes classifiers</li>
<li>K-Nearest Neighbour</li>
</ul>
</li>
<li>Support vector machines</li>
<li>Artificial neural networks
<ul>
<li>Supervised learning</li>
</ul>
</li>
<li>Decision trees
<ul>
<li>Aggregated into Random Forest</li>
</ul>
</li>
</ul>
<h3 id="steps-for-making-ai-less-scary">Steps for making AI less scary</h3>
<ol>
<li>Use it, understand it, integrate it
<ul>
<li>Embed AI in your day-to-day
<ul>
<li>Pop a tab right now, keep it open</li>
</ul>
</li>
<li>Use it
<ul>
<li>Especially when you don’t know what you’re doing</li>
</ul>
</li>
<li>It’s there to help you think
<ul>
<li>Augment human ability, not replace it</li>
<li>Mobile phones train carriage example</li>
<li>Google Calendar example</li>
</ul>
</li>
</ul>
</li>
</ol>
<h3 id="evolution-of-tech-jobs">Evolution of tech jobs</h3>
<ul>
<li>My job used to be about
<ul>
<li>Using technology, then</li>
<li>Leading teams that use technology, but now</li>
<li>I help companies think and organise</li>
</ul>
</li>
</ul>
<p>Who knows what business transformation will look like in the “AI positive future”, but this is how I’m going to find out.</p>
<hr />Alex StanhopeEvent streaming2023-05-23T00:00:00+00:002023-05-23T00:00:00+00:00https://www.lightenna.com/services/streaming<style>
/* hack page title for alignment on this particular image */
h1.page__title {
padding-top: 1.0em;
}
</style>
<div class="feature__wrapper">
<div class="feature__item">
<div class="archive__item">
<div class="archive__item-teaser">
<img src="/assets/images/videographer_32193_640x.jpg" alt="low-disruption-on-your-event" />
</div>
<div class="archive__item-body">
<h2 class="archive__item-title">Low disruption</h2>
<div class="archive__item-excerpt">
<p>Our experienced staff use multiple cameras and automation to minimise the impact on your event</p>
</div>
</div>
</div>
</div>
<div class="feature__item">
<div class="archive__item">
<div class="archive__item-teaser">
<img src="/assets/images/presenter_33594_640x.jpg" alt="low-cost-record-of-your-event" />
</div>
<div class="archive__item-body">
<h2 class="archive__item-title">Low cost record</h2>
<div class="archive__item-excerpt">
<p>We specialise in filming small events that otherwise might not get recorded</p>
</div>
</div>
</div>
</div>
<div class="feature__item">
<div class="archive__item">
<div class="archive__item-teaser">
<img src="/assets/images/curvyroad_01246_640x.jpg" alt="reliable-high-quality-streaming" />
</div>
<div class="archive__item-body">
<h2 class="archive__item-title">Reliable streaming</h2>
<div class="archive__item-excerpt">
<p>We use industry-grade equipment to reliably deliver a high-quality stream and post-event archive copy</p>
</div>
</div>
</div>
</div>
</div>
<h1 id="unobtrusive-recording-and-streaming">Unobtrusive recording and streaming</h1>
<p>Every event is different so we customise our offering based on your needs. Our goal is to ensure that fundamentally the staff who couldn’t be in the room can still get a clear sense of what’s going on. We can even provide back-channels to allow interaction from those joining online.</p>
<p>Each streamed event typically includes:</p>
<ul>
<li>Multi-camera setup</li>
<li>Multi-microphone setup including radio mics for presenters</li>
<li>Minimal disruption with only 1 or 2 engineers/camera-people</li>
<li>Streaming online using your existing network</li>
<li>Secure delivery to ensure closed meetings and sensitive content are handled appropriately</li>
</ul>
<h1 id="save-the-moments-that-not-everyone-can-attend-in-person">Save the moments that not everyone can attend in person</h1>
<p><a name="form"></a></p>
<form action="https://formspree.io/alex_stanhope@hotmail.com" method="POST">
<input type="text" name="name" placeholder="Your name" />
<input type="email" name="email" placeholder="Your email" />
<textarea name="message" placeholder="Your message"></textarea>
<p>There's a human-being on the end of this form, so please tell us how and when you'd like to be contacted
and anything pertinent the workload you'd like to host in our managed Kubernetes service.</p>
<button type="submit" class="btn btn--primary btn--large">Send</button>
</form>
<div stlye="clear:both;"> </div>Alex StanhopeTerraform-Docker integration exercises2021-02-19T00:00:00+00:002021-02-19T00:00:00+00:00https://www.lightenna.com/tech/2021/terraform-integration-exercises<h2 id="exercise-build-a-container-hosting-environment">Exercise: build a container hosting environment</h2>
<ul>
<li>Create an Azure Key Vault
<ul>
<li>Create a randomly-generated <a href="https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/key_vault_secret">azurerm_key_vault_secret</a> called <code class="language-plaintext highlighter-rouge">temp-password</code>.
<ul>
<li>While this secret isn’t used, it validates that AKV can create (and destroy) secrets.</li>
<li>Create an <code class="language-plaintext highlighter-rouge">output</code> resource to show some information about the created secret.</li>
</ul>
</li>
<li>The Key Vault must be created in its own isolated root module.</li>
<li>Declaring the Key Vault’s access policies inline (as part of the <a href="https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/key_vault">azurerm_key_vault</a> resource) makes deletion easier and removes the need for manual depends_on references.</li>
</ul>
</li>
<li>Provision an Azure Container registry using the <a href="https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/container_registry">azurerm_container_registry resource type</a>.
<ul>
<li>The Container registry must be created in a separate root module.</li>
<li>Enable the admin user.</li>
<li>Record the admin user’s username and password in your Key Vault.
<ul>
<li>Use a <a href="https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/key_vault">azurerm_key_vault data source</a> to reference your existing Key Vault.</li>
<li>Store each credential in its own secret.</li>
</ul>
</li>
</ul>
</li>
<li>Use <a href="https://docs.docker.com/engine/reference/commandline/login/">docker login</a> to authenticate against your new container registry.
<ul>
<li>Extend your Container registry module to produce a <code class="language-plaintext highlighter-rouge">docker login</code> command as output to make command-line login easier.
<ul>
<li>You’ll need to inline the password for now with either <code class="language-plaintext highlighter-rouge">--password</code> or <code class="language-plaintext highlighter-rouge">--password-stdin</code>. While this is too insecure for production, it’s sufficient for a short-lived password in an IAC exercise.</li>
<li>Also provider a <code class="language-plaintext highlighter-rouge">docker logout</code> command as output.</li>
</ul>
</li>
</ul>
</li>
<li>Create a containerised Node.js application
<ul>
<li>You might choose to use the <code class="language-plaintext highlighter-rouge">Dockerfile</code> and management scripts that your wrote as your solution to the <a href="https://github.com/lightenna/devops-workstream/tree/master/docker/tutorial/03-dockerfile-environment-variables">previous ‘environment variables’ exercise</a>.</li>
<li>Configure docker to accept a <code class="language-plaintext highlighter-rouge">PORT</code> environment variable.</li>
<li>Modify your management scripts to build the container and upload it to your container registry.
<ul>
<li>Be careful to re-build and re-push your container if you re-create your container registry.</li>
<li>Verify you can see your repository and tag in the <a href="https://portal.azure.com/">Azure Portal</a> <code class="language-plaintext highlighter-rouge">Container registry</code> dashboard, <code class="language-plaintext highlighter-rouge">Repositories</code> blade</li>
</ul>
</li>
</ul>
</li>
<li>Create an <a href="https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/app_service">azurerm_app_service</a> instance.
<ul>
<li>Again, the instance must be created using a separate root module.
<ul>
<li>Hint: start by using a community module, such as <a href="https://registry.terraform.io/modules/innovationnorway/web-app-container/azurerm/latest">this one by InnovationNorway</a>, to create all the azurerm_app_service resources.</li>
</ul>
</li>
<li>Initially configure the app service to run a public container image, before trying to authenticate against your Azure Container Registry.
<ul>
<li>Hint: use a public image that you’re familiar with (default ports etc.) from a previous exercise such as <a href="https://hub.docker.com/_/nginx">nginx:latest</a> to make testing easier.</li>
<li>Output the app_service hostname to test your deployment in a browser.
<ul>
<li>Give azure_app_service plenty of time (2-3 minutes) to pull and start the container.</li>
<li>Check the start-up process using the logs visible in the <a href="https://portal.azure.com/">Azure Portal</a> <code class="language-plaintext highlighter-rouge">App Service</code> dashboard, <code class="language-plaintext highlighter-rouge">Deployment Centre</code> blade</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li>Deploy a container from your Container registry to your App Service instance.
<ul>
<li>Set up authentication so that your App Service instance can pull container images from the Container Registry.</li>
<li>Output a URL that allows you to test you Node.js application from a browser.</li>
</ul>
</li>
</ul>
<p>This exercise is much more substantial than previous exercises. Please take it slowly, code a couple of lines then test and iterate, refactor as needed.</p>
<p>The solution to the ‘container hosting’ exercise is composed of both <a href="https://github.com/lightenna/devops-workstream/tree/master/terraform/tutorial/12-container-hosting">a Terraform solution</a>, <a href="https://github.com/lightenna/devops-workstream/tree/master/docker/tutorial/06-dockerfile-cloud-hosting">a Docker solution</a> is available as part of our open-source <a href="https://github.com/lightenna/devops-workstream/">Devops-Workstream</a>.</p>
<hr />Alex StanhopeExercise: build a container hosting environment Create an Azure Key Vault Create a randomly-generated azurerm_key_vault_secret called temp-password. While this secret isn’t used, it validates that AKV can create (and destroy) secrets. Create an output resource to show some information about the created secret. The Key Vault must be created in its own isolated root module. Declaring the Key Vault’s access policies inline (as part of the azurerm_key_vault resource) makes deletion easier and removes the need for manual depends_on references. Provision an Azure Container registry using the azurerm_container_registry resource type. The Container registry must be created in a separate root module. Enable the admin user. Record the admin user’s username and password in your Key Vault. Use a azurerm_key_vault data source to reference your existing Key Vault. Store each credential in its own secret. Use docker login to authenticate against your new container registry. Extend your Container registry module to produce a docker login command as output to make command-line login easier. You’ll need to inline the password for now with either --password or --password-stdin. While this is too insecure for production, it’s sufficient for a short-lived password in an IAC exercise. Also provider a docker logout command as output. Create a containerised Node.js application You might choose to use the Dockerfile and management scripts that your wrote as your solution to the previous ‘environment variables’ exercise. Configure docker to accept a PORT environment variable. Modify your management scripts to build the container and upload it to your container registry. Be careful to re-build and re-push your container if you re-create your container registry. Verify you can see your repository and tag in the Azure Portal Container registry dashboard, Repositories blade Create an azurerm_app_service instance. Again, the instance must be created using a separate root module. Hint: start by using a community module, such as this one by InnovationNorway, to create all the azurerm_app_service resources. Initially configure the app service to run a public container image, before trying to authenticate against your Azure Container Registry. Hint: use a public image that you’re familiar with (default ports etc.) from a previous exercise such as nginx:latest to make testing easier. Output the app_service hostname to test your deployment in a browser. Give azure_app_service plenty of time (2-3 minutes) to pull and start the container. Check the start-up process using the logs visible in the Azure Portal App Service dashboard, Deployment Centre blade Deploy a container from your Container registry to your App Service instance. Set up authentication so that your App Service instance can pull container images from the Container Registry. Output a URL that allows you to test you Node.js application from a browser.Docker exercises2021-02-08T00:00:00+00:002021-02-08T00:00:00+00:00https://www.lightenna.com/tech/2021/docker-exercises<h2 id="exercise-containerising-a-nodejs-webserver">Exercise: containerising a Node.js webserver</h2>
<ul>
<li>Create a simple Node.js web server
<ul>
<li>Create <code class="language-plaintext highlighter-rouge">index.js</code> to expose a web server on port 3030
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> // content of index.js
const http = require('http');
const port = 3030;
const requestHandler = (request, response) => {
console.log(request.url);
response.end('Hello Node.js Server!');
};
const server = http.createServer(requestHandler);
server.listen(port, (err) => {
if (err) { return console.log('something bad happened', err); }
console.log(`server is listening on ${port}`);
});
</code></pre></div> </div>
</li>
<li>Test locally
<ul>
<li>Install <code class="language-plaintext highlighter-rouge">package.json</code> using <code class="language-plaintext highlighter-rouge">npm install</code></li>
<li>Start using <code class="language-plaintext highlighter-rouge">npm start</code></li>
<li><code class="language-plaintext highlighter-rouge">curl localhost:3030</code> or open in local browser</li>
<li>Terminate local service after testing</li>
</ul>
</li>
</ul>
</li>
<li>Write a <code class="language-plaintext highlighter-rouge">Dockerfile</code> to:
<ul>
<li>Use a Node.js base image</li>
<li>Copy package.json over to image and <code class="language-plaintext highlighter-rouge">npm install</code> at build time</li>
<li>Run <code class="language-plaintext highlighter-rouge">npm start</code> at container start time</li>
<li><code class="language-plaintext highlighter-rouge">EXPOSE</code> port 3030 from the container</li>
</ul>
</li>
<li>Instantiate this image and connect to your container
<ul>
<li>Verify that you can connect to the web service from within the container</li>
<li>Use <code class="language-plaintext highlighter-rouge">docker container ls</code> to show exposed ports for running containers</li>
<li>Verify that you can connect to the web service from outside the container
<ul>
<li>e.g. on host machine</li>
</ul>
</li>
<li>Capture all your commands in scripts (good IAC practice)</li>
</ul>
</li>
<li>Run your container as a non-root user, e.g. user <code class="language-plaintext highlighter-rouge">1000</code>
<ul>
<li>Modify your container to run all processes as user <code class="language-plaintext highlighter-rouge">1000</code></li>
<li>Set appropriate file permissions to enable access to your non-root user</li>
</ul>
</li>
</ul>
<p>The <a href="https://github.com/lightenna/devops-workstream/tree/master/docker/tutorial/01-dockerfile-build">solution to the ‘containerising’ exercise</a> is available as part of our open-source <a href="https://github.com/lightenna/devops-workstream/">Devops-Workstream</a>.</p>
<h2 id="exercise-augment-existing-dockerfile">Exercise: augment existing Dockerfile</h2>
<ul>
<li>Create a <code class="language-plaintext highlighter-rouge">Dockerfile</code> that augments the existing NGINX Dockerfile
<ul>
<li>Use the <code class="language-plaintext highlighter-rouge">FROM</code> keyword to reference the <a href="https://hub.docker.com/_/nginx">latest nginx</a></li>
</ul>
</li>
<li>Install an additional package such as procps (for <code class="language-plaintext highlighter-rouge">ps</code> commands)
<ul>
<li>using the package manager built into your chosen distribution</li>
</ul>
</li>
<li>Copy a local nginx.conf file into your container
<ul>
<li>You might base your configuration on a simplified version of this <a href="https://www.nginx.com/resources/wiki/start/topics/examples/full/">Nginx Example Configuration</a>
<ul>
<li>Alternatively see solution for sample config</li>
</ul>
</li>
</ul>
</li>
<li>Copy a local website into your container
<ul>
<li>A single <code class="language-plaintext highlighter-rouge">index.html</code> file will suffice, but you can enrich it if you’d like</li>
</ul>
</li>
<li>Run NGINX as a non-root user, e.g. user <code class="language-plaintext highlighter-rouge">1000</code>
<ul>
<li>Hint: you’ll need to direct NGINX to use /tmp subfolder for all writable outputs</li>
</ul>
</li>
</ul>
<p>The <a href="https://github.com/lightenna/devops-workstream/tree/master/docker/tutorial/02-dockerfile-augment">solution to the ‘augment’ exercise</a> is available as part of our open-source <a href="https://github.com/lightenna/devops-workstream/">Devops-Workstream</a>.</p>
<h2 id="exercise-environment-variables">Exercise: environment variables</h2>
<ul>
<li>Build on the <a href="https://github.com/lightenna/devops-workstream/tree/master/docker/tutorial/01-dockerfile-build">‘containerising’ exercise</a> to pass in a <code class="language-plaintext highlighter-rouge">PORT</code> environment variable
<ul>
<li>Extend the Node.js code to look for the <code class="language-plaintext highlighter-rouge">PORT</code> environment variable
<ul>
<li>Add to package.json
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> ...
"main": "src/index.js",
"dependencies": {
"dotenv": "^8.2.0",
"nodemon": "^2.0.1"
},
...
</code></pre></div> </div>
</li>
</ul>
</li>
<li>Modify the <code class="language-plaintext highlighter-rouge">index.js</code> to accept an environment variable, but fall back to a default if absent
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> ...
const http = require('http');
const env = require('process').env;
require('dotenv').config();
const port = env.PORT || 8080;
...
</code></pre></div> </div>
</li>
</ul>
</li>
<li>Initially don’t set a environment value for port
<ul>
<li>Test to see if it’s exposed on 8080, both in the container and to the host</li>
</ul>
</li>
<li>Set a value for the environment variable in <code class="language-plaintext highlighter-rouge">Dockerfile</code>
<ul>
<li>Connect into the machine and <code class="language-plaintext highlighter-rouge">echo $PORT</code> to see environment value</li>
<li>Retest access on 8080 and your new value, both in the container and to the host
<ul>
<li>Hint: use <code class="language-plaintext highlighter-rouge">docker container ls</code> to see what your container exposes by default</li>
</ul>
</li>
</ul>
</li>
<li>Set a value for <code class="language-plaintext highlighter-rouge">PORT</code> in the <code class="language-plaintext highlighter-rouge">docker run</code> command that you use to instantiate your container
<ul>
<li>Retest access again</li>
</ul>
</li>
</ul>
<p>The <a href="https://github.com/lightenna/devops-workstream/tree/master/docker/tutorial/03-dockerfile-environment-variables">solution to the ‘environment variables’ exercise</a> is available as part of our open-source <a href="https://github.com/lightenna/devops-workstream/">Devops-Workstream</a>.</p>
<h2 id="exercise-volumes">Exercise: volumes</h2>
<ul>
<li>Build on the <a href="https://github.com/lightenna/devops-workstream/tree/master/docker/tutorial/02-dockerfile-augment">‘augment’ exercise</a> to share volumes into your container
<ul>
<li>Instead of using <code class="language-plaintext highlighter-rouge">COPY</code> to make a duplicate of your <code class="language-plaintext highlighter-rouge">nginx.conf</code>
<ul>
<li>mount the file as a volume</li>
</ul>
</li>
</ul>
</li>
<li>Try modifying your config then restarting your container
<ul>
<li>Hint: you can use <code class="language-plaintext highlighter-rouge">docker logs <container_name></code> to see start-up logs from NGINX</li>
</ul>
</li>
<li>Repeat this process for the local website you made previously
<ul>
<li>Instead of using <code class="language-plaintext highlighter-rouge">COPY</code> to make a duplicate of your local website
<ul>
<li>mount the directory as a volume</li>
</ul>
</li>
</ul>
</li>
<li>Modify your local website and restart your container</li>
</ul>
<p>The <a href="https://github.com/lightenna/devops-workstream/tree/master/docker/tutorial/04-dockerfile-volumes">solution to the ‘volumes’ exercise</a> is available as part of our open-source <a href="https://github.com/lightenna/devops-workstream/">Devops-Workstream</a>.</p>
<h2 id="exercise-docker-compose">Exercise: docker-compose</h2>
<ul>
<li>Write a <code class="language-plaintext highlighter-rouge">docker-compose.yml</code> file to bring up an ELK stack, using container images:
<ul>
<li>Elasticsearch <code class="language-plaintext highlighter-rouge">docker.elastic.co/elasticsearch/elasticsearch:7.10.0</code></li>
<li>Logstash <code class="language-plaintext highlighter-rouge">docker.elastic.co/logstash/logstash:7.10.0</code></li>
<li>Kibana <code class="language-plaintext highlighter-rouge">docker.elastic.co/kibana/kibana:7.10.0</code></li>
</ul>
</li>
<li>Bring your stack up using <code class="language-plaintext highlighter-rouge">docker-compose up</code>
<ul>
<li>You might choose to run that in a separate process (using <code class="language-plaintext highlighter-rouge">&</code>) to avoid blocking your command prompt</li>
</ul>
</li>
<li>Pass an environment variable to Elasticsearch:
<ul>
<li><code class="language-plaintext highlighter-rouge">discovery.type=single-node</code></li>
</ul>
</li>
<li>Expose port <code class="language-plaintext highlighter-rouge">9200</code> for Elasticsearch and <code class="language-plaintext highlighter-rouge">5601</code> for Kibana
<ul>
<li>Test internal (in-container) and external (on-guest) access to both services</li>
</ul>
</li>
<li>Ensure that Elasticsearch spins-up before Logstash and Kibana using the <code class="language-plaintext highlighter-rouge">depends_on</code> relationship</li>
</ul>
<p>The <a href="https://github.com/lightenna/devops-workstream/tree/master/docker/tutorial/05-dockerfile-environment-variables">solution to the ‘docker-compose’ exercise</a> is available as part of our open-source <a href="https://github.com/lightenna/devops-workstream/">Devops-Workstream</a>.</p>
<hr />Alex StanhopeExercise: containerising a Node.js webserver Create a simple Node.js web server Create index.js to expose a web server on port 3030 // content of index.js const http = require('http'); const port = 3030; const requestHandler = (request, response) => { console.log(request.url); response.end('Hello Node.js Server!'); }; const server = http.createServer(requestHandler); server.listen(port, (err) => { if (err) { return console.log('something bad happened', err); } console.log(`server is listening on ${port}`); }); Test locally Install package.json using npm install Start using npm start curl localhost:3030 or open in local browser Terminate local service after testing Write a Dockerfile to: Use a Node.js base image Copy package.json over to image and npm install at build time Run npm start at container start time EXPOSE port 3030 from the container Instantiate this image and connect to your container Verify that you can connect to the web service from within the container Use docker container ls to show exposed ports for running containers Verify that you can connect to the web service from outside the container e.g. on host machine Capture all your commands in scripts (good IAC practice) Run your container as a non-root user, e.g. user 1000 Modify your container to run all processes as user 1000 Set appropriate file permissions to enable access to your non-root userWhat is Kubernetes?2020-11-06T00:00:00+00:002020-11-06T00:00:00+00:00https://www.lightenna.com/tech/2020/what-is-kubernetes<!-- Courtesy of embedresponsively.com //-->
<div class="responsive-video-container">
<iframe src="https://www.youtube-nocookie.com/embed/t2D5S4224WU" frameborder="0" allowfullscreen=""></iframe>
</div>
<h2 id="what-is-kubernetes">What is Kubernetes?</h2>
<ul>
<li>What are containers?</li>
<li>What is Docker?</li>
<li>Where are my apps?</li>
<li>Why orchestrate?</li>
</ul>
<hr />
<h2 id="applications">Applications</h2>
<ul>
<li>Hosted on a server</li>
<li>Installed on the disk</li>
<li>Run as processes</li>
<li>Provide a service</li>
<li>Quality of service</li>
</ul>
<hr />
<p>A single application running on a host</p>
<div class="svg-embed">
<figure class="image" style="width: 25%; min-width: 150px">
<object class="wrapper" type="image/svg+xml" data="/assets/svg/kubernetes/k8s-app-singlehost.svg">
<img src="/assets/svg/kubernetes/k8s-app-singlehost.png" alt="Falling back to PNG as your browser does not support SVG." />
</object>
</figure>
</div>
<div class="download-link">
<a href="/assets/svg/kubernetes/k8s-app-singlehost.svg">Download SVG</a>
| <a class="download-link" href="/assets/svg/kubernetes/k8s-app-singlehost.png">PNG</a>
</div>
<hr />
<h2 id="containers">Containers</h2>
<ul>
<li>Standardised</li>
<li>Isolated</li>
<li>Easily distributed</li>
<li>Improved security</li>
</ul>
<p>Containers ensure standardisation for distribution and isolation for execution.</p>
<hr />
<p>A single container running on a host</p>
<div class="svg-embed">
<figure class="image" style="width: 25%; min-width: 150px">
<object class="wrapper" type="image/svg+xml" data="/assets/svg/kubernetes/k8s-docker-singlehost.svg">
<img src="/assets/svg/kubernetes/k8s-docker-singlehost.png" alt="Falling back to PNG as your browser does not support SVG." />
</object>
</figure>
</div>
<div class="download-link">
<a href="/assets/svg/kubernetes/k8s-docker-singlehost.svg">Download SVG</a>
| <a class="download-link" href="/assets/svg/kubernetes/k8s-docker-singlehost.png">PNG</a>
</div>
<hr />
<h2 id="docker">Docker</h2>
<ul>
<li>Docker is a container platform</li>
<li>Standard for containers</li>
<li>Not a hypervisor</li>
<li>Docker Hub is a library</li>
</ul>
<p>Docker is not a hypervisor, but it behaves a little bit like one.
It allows different workloads to be run side-by-side without interfering with each other.
Docker can be installed for a <a href="https://docs.docker.com/get-docker/">variety of platforms</a>.
In production, or Production-like environments, that’s typically as a package for Linux.</p>
<hr />
<h2 id="multiple-containers">Multiple containers</h2>
<ul>
<li>All isolated from one another</li>
<li>Managed interactions</li>
<li>Workload control</li>
<li>Security</li>
</ul>
<hr />
<p>A single host running multiple containers side-by-side</p>
<div class="svg-embed">
<figure class="image" style="width: 25%; min-width: 150px">
<object class="wrapper" type="image/svg+xml" data="/assets/svg/kubernetes/k8s-docker-singlehost-multicontainer.svg">
<img src="/assets/svg/kubernetes/k8s-docker-singlehost-multicontainer.png" alt="Falling back to PNG as your browser does not support SVG." />
</object>
</figure>
</div>
<div class="download-link">
<a href="/assets/svg/kubernetes/k8s-docker-singlehost-multicontainer.svg">Download SVG</a>
| <a class="download-link" href="/assets/svg/kubernetes/k8s-docker-singlehost-multicontainer.png">PNG</a>
</div>
<hr />
<h2 id="things-go-wrong">Things go wrong</h2>
<ul>
<li>Hosts crash</li>
<li>Goal is quality service delivery:
<ul>
<li>Reliability</li>
<li>Performance</li>
<li>High-availability</li>
</ul>
</li>
<li>Redundant copies</li>
</ul>
<hr />
<p>Multiple Docker hosts behind a load balancer</p>
<div class="svg-embed">
<figure class="image">
<object class="wrapper" type="image/svg+xml" data="/assets/svg/kubernetes/k8s-docker-multi-host.svg">
<img src="/assets/svg/kubernetes/k8s-docker-multi-host.png" alt="Falling back to PNG as your browser does not support SVG." />
</object>
</figure>
</div>
<div class="download-link">
<a href="/assets/svg/kubernetes/k8s-docker-multi-host.svg">Download SVG</a>
| <a class="download-link" href="/assets/svg/kubernetes/k8s-docker-multi-host.png">PNG</a>
</div>
<hr />
<h2 id="more-to-manage">More to manage</h2>
<ul>
<li>Config management
<ul>
<li>Networking</li>
</ul>
</li>
<li>Secret distribution</li>
<li>Simplicity of load balancer</li>
<li>Identical nodes
<ul>
<li>Non-adaptive</li>
</ul>
</li>
<li>Need help orchestrating</li>
</ul>
<hr />
<p>Kubernetes</p>
<div class="svg-embed">
<figure class="image" style="width: 25%; min-width: 150px">
<object class="wrapper" type="image/svg+xml" data="/assets/svg/kubernetes/k8s-icon.svg">
<img src="/assets/svg/kubernetes/k8s-icon.png" alt="Falling back to PNG as your browser does not support SVG." />
</object>
</figure>
</div>
<div class="download-link">
<a href="/assets/svg/kubernetes/k8s-icon.svg">Download SVG</a>
| <a class="download-link" href="/assets/svg/kubernetes/k8s-icon.png">PNG</a>
</div>
<hr />
<h2 id="kubernetes">Kubernetes</h2>
<ul>
<li>Container orchestration</li>
<li>Networking</li>
<li>Services
<ul>
<li>Load balancing</li>
<li>High availability</li>
</ul>
</li>
<li>Ingress</li>
<li>Secret management</li>
</ul>
<hr />
<p>A Kubernetes pod wrapping a single container</p>
<div class="svg-embed">
<figure class="image" style="width: 40%;">
<object class="wrapper" type="image/svg+xml" data="/assets/svg/kubernetes/k8s-pod-elasticsearch.svg">
<img src="/assets/svg/kubernetes/k8s-pod-elasticsearch.png" alt="Falling back to PNG as your browser does not support SVG." />
</object>
</figure>
</div>
<div class="download-link">
<a href="/assets/svg/kubernetes/k8s-pod-elasticsearch.svg">Download SVG</a>
| <a class="download-link" href="/assets/svg/kubernetes/k8s-pod-elasticsearch.png">PNG</a>
</div>
<hr />
<h2 id="kubernetes-entities">Kubernetes entities</h2>
<ul>
<li>Pod
<ul>
<li>Wraps a container</li>
</ul>
</li>
<li>Deployment
<ul>
<li>Defines pod and how it’s deployed to cluster</li>
<li>Multiple instances of a pod</li>
<li>Multiple different pods</li>
</ul>
</li>
</ul>
<hr />
<p>Multiple Kubernetes pods deployed across multiple hosts</p>
<div class="svg-embed">
<figure class="image">
<object class="wrapper" type="image/svg+xml" data="/assets/svg/kubernetes/k8s-distributed-pods-multi-host.svg">
<img src="/assets/svg/kubernetes/k8s-distributed-pods-multi-host.png" alt="Falling back to PNG as your browser does not support SVG." />
</object>
</figure>
</div>
<div class="download-link">
<a href="/assets/svg/kubernetes/k8s-distributed-pods-multi-host.svg">Download SVG</a>
| <a class="download-link" href="/assets/svg/kubernetes/k8s-distributed-pods-multi-host.png">PNG</a>
</div>
<hr />
<h2 id="kubernetes-entities-1">Kubernetes entities</h2>
<ul>
<li>Services
<ul>
<li>Routed across multiple pods</li>
<li>Hide underlying implementation</li>
</ul>
</li>
</ul>
<hr />
<p>Services broker requests to an orchestrated set of pods</p>
<div class="svg-embed">
<figure class="image">
<object class="wrapper" type="image/svg+xml" data="/assets/svg/kubernetes/k8s-service-across-pods-by-host.svg">
<img src="/assets/svg/kubernetes/k8s-service-across-pods-by-host.png" alt="Falling back to PNG as your browser does not support SVG." />
</object>
</figure>
</div>
<div class="download-link">
<a href="/assets/svg/kubernetes/k8s-service-across-pods-by-host.svg">Download SVG</a>
| <a class="download-link" href="/assets/svg/kubernetes/k8s-service-across-pods-by-host.png">PNG</a>
</div>
<hr />
<h2 id="help">Help</h2>
<p>If you’d like help organising and preparing your digital strategy, working with your team to foster a data-driven culture, or just hands-on-keyboard training in infrastructure-as-code, please <a href="/contact">get in touch</a>.</p>
<p><a class="reveal-link" href="reveal/">View as a presentation</a></p>Alex StanhopeWhat is Kubernetes?2020-11-06T00:00:00+00:002020-11-06T00:00:00+00:00https://www.lightenna.com/tech/2020/what-is-kubernetes/what-is-kubernetes_presentation<div class="reveal"><div class="slides"><section data-markdown="" data-separator="^\n---\n$" data-separator-vertical="^\n----\n$" data-notes="^Note:"><script type="text/template">
## What is Kubernetes?
+ What are containers?
+ What is Docker?
+ Where are my apps?
+ Why orchestrate?
Note:
---
## Applications
+ Hosted on a server
+ Installed on the disk
+ Run as processes
+ Provide a service
+ Quality of service
Note:
---
A single application running on a host
<div class="svg-embed">
<figure class="image" style="width: 25%; min-width: 150px">
<object class="wrapper" type="image/svg+xml" data="/assets/svg/kubernetes/k8s-app-singlehost.svg">
<img src="/assets/svg/kubernetes/k8s-app-singlehost.png" alt="Falling back to PNG as your browser does not support SVG." />
</object>
</figure>
</div>
<div class="download-link">
<a href="/assets/svg/kubernetes/k8s-app-singlehost.svg">Download SVG</a>
| <a class="download-link" href="/assets/svg/kubernetes/k8s-app-singlehost.png">PNG</a>
</div>
---
## Containers
+ Standardised
+ Isolated
+ Easily distributed
+ Improved security
Note:
Containers ensure standardisation for distribution and isolation for execution.
---
A single container running on a host
<div class="svg-embed">
<figure class="image" style="width: 25%; min-width: 150px">
<object class="wrapper" type="image/svg+xml" data="/assets/svg/kubernetes/k8s-docker-singlehost.svg">
<img src="/assets/svg/kubernetes/k8s-docker-singlehost.png" alt="Falling back to PNG as your browser does not support SVG." />
</object>
</figure>
</div>
<div class="download-link">
<a href="/assets/svg/kubernetes/k8s-docker-singlehost.svg">Download SVG</a>
| <a class="download-link" href="/assets/svg/kubernetes/k8s-docker-singlehost.png">PNG</a>
</div>
---
## Docker
+ Docker is a container platform
+ Standard for containers
+ Not a hypervisor
+ Docker Hub is a library
Note:
Docker is not a hypervisor, but it behaves a little bit like one.
It allows different workloads to be run side-by-side without interfering with each other.
Docker can be installed for a [variety of platforms](https://docs.docker.com/get-docker/).
In production, or Production-like environments, that's typically as a package for Linux.
---
## Multiple containers
+ All isolated from one another
+ Managed interactions
+ Workload control
+ Security
---
A single host running multiple containers side-by-side
<div class="svg-embed">
<figure class="image" style="width: 25%; min-width: 150px">
<object class="wrapper" type="image/svg+xml" data="/assets/svg/kubernetes/k8s-docker-singlehost-multicontainer.svg">
<img src="/assets/svg/kubernetes/k8s-docker-singlehost-multicontainer.png" alt="Falling back to PNG as your browser does not support SVG." />
</object>
</figure>
</div>
<div class="download-link">
<a href="/assets/svg/kubernetes/k8s-docker-singlehost-multicontainer.svg">Download SVG</a>
| <a class="download-link" href="/assets/svg/kubernetes/k8s-docker-singlehost-multicontainer.png">PNG</a>
</div>
---
## Things go wrong
+ Hosts crash
+ Goal is quality service delivery:
+ Reliability
+ Performance
+ High-availability
+ Redundant copies
---
Multiple Docker hosts behind a load balancer
<div class="svg-embed">
<figure class="image" >
<object class="wrapper" type="image/svg+xml" data="/assets/svg/kubernetes/k8s-docker-multi-host.svg">
<img src="/assets/svg/kubernetes/k8s-docker-multi-host.png" alt="Falling back to PNG as your browser does not support SVG." />
</object>
</figure>
</div>
<div class="download-link">
<a href="/assets/svg/kubernetes/k8s-docker-multi-host.svg">Download SVG</a>
| <a class="download-link" href="/assets/svg/kubernetes/k8s-docker-multi-host.png">PNG</a>
</div>
---
## More to manage
+ Config management
+ Networking
+ Secret distribution
+ Simplicity of load balancer
+ Identical nodes
+ Non-adaptive
+ Need help orchestrating
---
Kubernetes
<div class="svg-embed">
<figure class="image" style="width: 25%; min-width: 150px">
<object class="wrapper" type="image/svg+xml" data="/assets/svg/kubernetes/k8s-icon.svg">
<img src="/assets/svg/kubernetes/k8s-icon.png" alt="Falling back to PNG as your browser does not support SVG." />
</object>
</figure>
</div>
<div class="download-link">
<a href="/assets/svg/kubernetes/k8s-icon.svg">Download SVG</a>
| <a class="download-link" href="/assets/svg/kubernetes/k8s-icon.png">PNG</a>
</div>
---
## Kubernetes
+ Container orchestration
+ Networking
+ Services
+ Load balancing
+ High availability
+ Ingress
+ Secret management
---
A Kubernetes pod wrapping a single container
<div class="svg-embed">
<figure class="image" style="width: 40%;">
<object class="wrapper" type="image/svg+xml" data="/assets/svg/kubernetes/k8s-pod-elasticsearch.svg">
<img src="/assets/svg/kubernetes/k8s-pod-elasticsearch.png" alt="Falling back to PNG as your browser does not support SVG." />
</object>
</figure>
</div>
<div class="download-link">
<a href="/assets/svg/kubernetes/k8s-pod-elasticsearch.svg">Download SVG</a>
| <a class="download-link" href="/assets/svg/kubernetes/k8s-pod-elasticsearch.png">PNG</a>
</div>
---
## Kubernetes entities
+ Pod
+ Wraps a container
+ Deployment
+ Defines pod and how it's deployed to cluster
+ Multiple instances of a pod
+ Multiple different pods
---
Multiple Kubernetes pods deployed across multiple hosts
<div class="svg-embed">
<figure class="image" >
<object class="wrapper" type="image/svg+xml" data="/assets/svg/kubernetes/k8s-distributed-pods-multi-host.svg">
<img src="/assets/svg/kubernetes/k8s-distributed-pods-multi-host.png" alt="Falling back to PNG as your browser does not support SVG." />
</object>
</figure>
</div>
<div class="download-link">
<a href="/assets/svg/kubernetes/k8s-distributed-pods-multi-host.svg">Download SVG</a>
| <a class="download-link" href="/assets/svg/kubernetes/k8s-distributed-pods-multi-host.png">PNG</a>
</div>
---
## Kubernetes entities
+ Services
+ Routed across multiple pods
+ Hide underlying implementation
---
Services broker requests to an orchestrated set of pods
<div class="svg-embed">
<figure class="image" >
<object class="wrapper" type="image/svg+xml" data="/assets/svg/kubernetes/k8s-service-across-pods-by-host.svg">
<img src="/assets/svg/kubernetes/k8s-service-across-pods-by-host.png" alt="Falling back to PNG as your browser does not support SVG." />
</object>
</figure>
</div>
<div class="download-link">
<a href="/assets/svg/kubernetes/k8s-service-across-pods-by-host.svg">Download SVG</a>
| <a class="download-link" href="/assets/svg/kubernetes/k8s-service-across-pods-by-host.png">PNG</a>
</div>
---
## Help
Note:
If you'd like help organising and preparing your digital strategy, working with your team to foster a data-driven culture, or just hands-on-keyboard training in infrastructure-as-code, please [get in touch](/contact).
</script></section></div></div>Alex StanhopeKubernetes as a service2020-10-30T00:00:00+00:002020-10-30T00:00:00+00:00https://www.lightenna.com/services/kubernetes-as-a-service<style>
/* hack page title for alignment on this particular image */
h1.page__title {
padding-top: 1.0em;
}
</style>
<div class="feature__wrapper">
<div class="feature__item--left">
<div class="archive__item">
<div class="archive__item-teaser">
<!-- Courtesy of embedresponsively.com //-->
<div class="responsive-video-container">
<iframe src="https://www.youtube-nocookie.com/embed/U8FtybTpA3c" frameborder="0" allowfullscreen=""></iframe>
</div>
</div>
<div class="archive__item-body">
<div class="archive__item-excerpt">
<p>Containers are transforming the way we develop and deploy code, but managing them in production requires a variety of skills. We specialise in Cloud platforms, so you can concentrate on the core business activity that adds value to your customers. We offer an array of services ranging from a simple out-of-the-box installation and setup of an on-prem Kubernetes cluster, through to a complete managed service including low-level patching, software updates and even attended deployments.</p>
</div>
</div>
</div>
</div>
</div>
<div class="feature__wrapper">
<div class="feature__item">
<div class="archive__item">
<div class="archive__item-teaser">
<img src="/assets/images/superhand_14154_12899_640x.jpg" alt="less-downtime-server-hand-zero" />
</div>
<div class="archive__item-body">
<h2 class="archive__item-title">Less downtime</h2>
<div class="archive__item-excerpt">
<p>By using rolling deployments, you can reduce your service downtime to zero.</p>
</div>
</div>
</div>
</div>
<div class="feature__item">
<div class="archive__item">
<div class="archive__item-teaser">
<img src="/assets/images/managedatacentre_157315_640x.jpg" alt="better-utilisation-server-monitoring" />
</div>
<div class="archive__item-body">
<h2 class="archive__item-title">Better utilisation</h2>
<div class="archive__item-excerpt">
<p>Fewer distractions from the racks and pipes, so you can focus on delivering business value.</p>
</div>
</div>
</div>
</div>
<div class="feature__item">
<div class="archive__item">
<div class="archive__item-teaser">
<img src="/assets/images/curvyroad_01246_640x.jpg" alt="route-to-cloud-highway-traffic" />
</div>
<div class="archive__item-body">
<h2 class="archive__item-title">Route to Cloud</h2>
<div class="archive__item-excerpt">
<p>Make on-prem Kubernetes part of your wider digital strategy, perhaps as a 2-5 year stop-gap before migrating to the Cloud.</p>
</div>
</div>
</div>
</div>
</div>
<h1 id="solid-architecture-solid-future">Solid architecture, solid future</h1>
<p>No web project should be deployed without the backing of good architecture to ensure we build the right thing and it’s built right. Done right, a new platform can be transformative so we provide clear documentation to show how your environments will work. We agree with you what hostnames should be assigned, how DNS will work, what level of firewalling should be built into the solution and the base operating system for each machine build. We then provide a quote that shows precisely what we will deploy if you decide to go ahead.</p>
<div class="svg-embed">
<figure class="image">
<object class="wrapper" type="image/svg+xml" data="/assets/svg/azure-cloud-architecture2-simplified.svg">
<img src="/assets/svg/azure-cloud-architecture2-simplified.png" alt="Falling back to PNG as your browser does not support SVG." />
</object>
</figure>
</div>
<div class="download-link">
<a href="/assets/svg/azure-cloud-architecture2-simplified.svg">Download SVG</a>
| <a class="download-link" href="/assets/svg/azure-cloud-architecture2-simplified.png">PNG</a>
</div>
<div class="feature__wrapper"> </div>
<p>To make sure your Kubernetes cluster continues to run reliably, every Lightenna Kubernetes environment also includes:</p>
<ul>
<li>Configuration-managed set of firewalled hosts</li>
<li>Pre-configured Kubernetes cluster to run across the worker nodes</li>
<li>Infrastructure-as-code manifests to provision and manage machines</li>
<li>Distributed block storage class (Cloud storage or Longhorn on-prem)</li>
</ul>
<div class="feature__wrapper"> </div>
<h1 id="free-and-open-source-software-foss-foundations">Free and Open-source Software (FOSS) foundations</h1>
<p>Everything we do is based on open standards, to ensure that you never become locked into a single vendor.
Along with your cluster, we deliver the infrastructure-as-code that you need to maintain, replicate and extend what you’ve paid for. We hope our great service will mean you choose us again in the future.</p>
<div class="feature__wrapper">
<div class="feature__item">
<div class="archive__item">
<div class="archive__item-teaser">
<img src="/assets/screenshots/screenshot-puppetboard_640x.png" alt="puppetboard-screenshot" />
</div>
<div class="archive__item-body">
<h2 class="archive__item-title">Puppet</h2>
<div class="archive__item-excerpt">
<p>Puppetboard as a user interface (UI) for Puppet and PuppetDB</p>
</div>
</div>
</div>
</div>
<div class="feature__item">
<div class="archive__item">
<div class="archive__item-teaser">
<img src="/assets/screenshots/screenshot-rancher_640x.png" alt="rancher-screenshot" />
</div>
<div class="archive__item-body">
<h2 class="archive__item-title">Rancher and Kubectl</h2>
<div class="archive__item-excerpt">
<p>Rancher visual UI and Kubectl command-line for cluster maintenance and deployments</p>
</div>
</div>
</div>
</div>
<div class="feature__item">
<div class="archive__item">
<div class="archive__item-teaser">
<img src="/assets/screenshots/screenshot-kibana_640x.png" alt="kibana-screenshot" />
</div>
<div class="archive__item-body">
<h2 class="archive__item-title">Elasticsearch</h2>
<div class="archive__item-excerpt">
<p>Elasticsearch, Logstash & Kibana (ELK) for logging and Prometheus & AlertManager for monitoring</p>
</div>
</div>
</div>
</div>
</div>
<h1 id="support">Support</h1>
<p>The success of any kind of Cloud project depends not only on a good delivery but how well it's supported in production. We offer three different service offerings, depending on what you decide is the best fit for your organisation:</p>
<ul>
<li>Staged handover: we train your team to build confidence in the technologies that underpin the cluster, then we’re available for ad hoc support, if rather than when it’s required.</li>
<li>Part-managed: we patch and maintain the cluster, by proactively monitoring for issues before they affect the service.</li>
<li>Fully-managed: as above, but we also handle code deployments to the cluster and application upgrades.</li>
</ul>
<div class="feature__wrapper">
<div class="feature__item--right">
<div class="archive__item">
<div class="archive__item-teaser">
<img src="/assets/svg/cloud_framework_cybertunnel_880x.jpg" alt="cloud-framework" />
</div>
<div class="archive__item-body">
<h2 class="archive__item-title">Everything you need for Cloud</h2>
<div class="archive__item-excerpt">
<p>Kubernetes may already fit into your digital strategy, but if you’d like advice on how to integrate an on-prem cluster into your existing Cloud infrastructure, please <a href="/contact/">get in touch</a>. Our consultants can bring to bear years of experience in Cloud adoption to help your business take on the right mix of services from hyperscale providers such as AWS, Google Cloud and Microsoft Azure.</p>
</div>
<p><a href="/tech/2018/cloud-framework/" class="btn btn--inverse">Read More</a></p>
</div>
</div>
</div>
</div>
<h1 id="let-us-show-you-the-business-benefits-of-kubernetes">Let us show you the business benefits of Kubernetes</h1>
<p><a name="form"></a></p>
<form action="https://formspree.io/alex_stanhope@hotmail.com" method="POST">
<input type="text" name="name" placeholder="Your name" />
<input type="email" name="email" placeholder="Your email" />
<textarea name="message" placeholder="Your message"></textarea>
<p>There's a human-being on the end of this form, so please tell us how and when you'd like to be contacted
and anything pertinent the workload you'd like to host in our managed Kubernetes service.</p>
<button type="submit" class="btn btn--primary btn--large">Send</button>
</form>
<div stlye="clear:both;"> </div>Alex StanhopeBuilding blocks for hosting containers2020-10-06T00:00:00+00:002020-10-06T00:00:00+00:00https://www.lightenna.com/tech/2020/building-blocks-for-hosting-containers<h2 id="start-simple">Start simple</h2>
<ul>
<li>Massively powerful tools</li>
<li>Simple to use</li>
<li>Complex use cases</li>
<li>Focus on the key activities</li>
</ul>
<p>This piece is about a massively powerful set of tools that collectively manage container-based applications.
Each of these tools is vast in its power and scope.
As such, each can be used in a plethora of different ways.
By means of an introduction to each, my goal is to focus on a few functions of each tool, how they work and what they do.</p>
<hr />
<h2 id="docker">Docker</h2>
<ul>
<li>Docker runtime</li>
<li>Running containers</li>
<li>Concurrent but isolated</li>
<li>Installation options
<ul>
<li>Dev</li>
<li>UAT and Production</li>
</ul>
</li>
</ul>
<p>Docker is not a hypervisor, but it behaves a little bit like one.
It allows different workloads to be run side-by-side without interfering with each other.
Containers ensure standardisation for distribution and isolation for execution.
Docker can be installed for a <a href="https://docs.docker.com/get-docker/">variety of platforms</a>.
In production, or Production-like environments, that’s typically as a package for Linux.</p>
<hr />
<h2 id="docker-interactions">Docker interactions</h2>
<ul>
<li>Runs containers
<ul>
<li>Entry point</li>
<li>Run as 1000:1000</li>
</ul>
</li>
<li>Read-only file system</li>
<li>Volumes</li>
<li>Port mapping</li>
</ul>
<hr />
<h2 id="docker-command-line">Docker command-line</h2>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker run -d --restart=unless-stopped \
-p 80:80 -p 443:443 \
rancher/rancher:latest
</code></pre></div></div>
<hr />
<p>A single container running on a Docker host</p>
<div class="svg-embed">
<figure class="image" style="width: 25%; min-width: 150px">
<object class="wrapper" type="image/svg+xml" data="/assets/svg/kubernetes/k8s-docker-singlehost.svg">
<img src="/assets/svg/kubernetes/k8s-docker-singlehost.png" alt="Falling back to PNG as your browser does not support SVG." />
</object>
</figure>
</div>
<div class="download-link">
<a href="/assets/svg/kubernetes/k8s-docker-singlehost.svg">Download SVG</a>
| <a class="download-link" href="/assets/svg/kubernetes/k8s-docker-singlehost.png">PNG</a>
</div>
<hr />
<h2 id="docker-build-process">Docker build process</h2>
<ul>
<li>Create container image: <code class="language-plaintext highlighter-rouge">docker build</code></li>
<li>Instantiated container: <code class="language-plaintext highlighter-rouge">docker run</code></li>
<li>Connect into container: <code class="language-plaintext highlighter-rouge">docker exec -it</code></li>
<li>Stop container: <code class="language-plaintext highlighter-rouge">docker stop</code></li>
<li>Delete container: <code class="language-plaintext highlighter-rouge">docker rm</code></li>
<li>Delete container image: <code class="language-plaintext highlighter-rouge">docker rmi</code></li>
</ul>
<hr />
<h2 id="docker-compose">Docker compose</h2>
<ul>
<li>Run up multiple containers</li>
<li>File-based, YAML</li>
<li>Simple to start up <code class="language-plaintext highlighter-rouge">docker-compose up</code></li>
<li>Simple to stop them <code class="language-plaintext highlighter-rouge">docker-compose down</code></li>
</ul>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>version: "3"
services:
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:7.5.1
environment:
- discovery.type=single-node
ports:
- 9200:9200
logstash:
image: docker.elastic.co/logstash/logstash:7.5.1
ports:
- 5044:5044
kibana:
image: docker.elastic.co/kibana/kibana:7.5.1
ports:
- 5601:5601
depends_on:
- elasticsearch
</code></pre></div></div>
<hr />
<p>A single Docker host running multiple containers side-by-side</p>
<div class="svg-embed">
<figure class="image" style="width: 25%; min-width: 150px">
<object class="wrapper" type="image/svg+xml" data="/assets/svg/kubernetes/k8s-docker-singlehost-multicontainer.svg">
<img src="/assets/svg/kubernetes/k8s-docker-singlehost-multicontainer.png" alt="Falling back to PNG as your browser does not support SVG." />
</object>
</figure>
</div>
<div class="download-link">
<a href="/assets/svg/kubernetes/k8s-docker-singlehost-multicontainer.svg">Download SVG</a>
| <a class="download-link" href="/assets/svg/kubernetes/k8s-docker-singlehost-multicontainer.png">PNG</a>
</div>
<hr />
<h2 id="kubernetes">Kubernetes</h2>
<ul>
<li>Container orchestration</li>
<li>Networking</li>
<li>Services
<ul>
<li>Load balancing</li>
<li>High availability</li>
</ul>
</li>
<li>Ingress</li>
<li>Secret management</li>
</ul>
<hr />
<h2 id="kubernetes-entities">Kubernetes entities</h2>
<ul>
<li>Pod
<ul>
<li>wraps a container</li>
</ul>
</li>
<li>Deployment
<ul>
<li>defines pod and how it’s deployed to cluster</li>
<li>multiple instances of a pod</li>
<li>multiple different pods</li>
</ul>
</li>
</ul>
<hr />
<p>A Kubernetes pod wrapping a single container</p>
<div class="svg-embed">
<figure class="image" style="width: 40%;">
<object class="wrapper" type="image/svg+xml" data="/assets/svg/kubernetes/k8s-pod.svg">
<img src="/assets/svg/kubernetes/k8s-pod.png" alt="Falling back to PNG as your browser does not support SVG." />
</object>
</figure>
</div>
<div class="download-link">
<a href="/assets/svg/kubernetes/k8s-pod.svg">Download SVG</a>
| <a class="download-link" href="/assets/svg/kubernetes/k8s-pod.png">PNG</a>
</div>
<hr />
<p>A single Kubernetes pod wrapping multiple containers (sidecar pattern)</p>
<div class="svg-embed">
<figure class="image" style="width: 40%;">
<object class="wrapper" type="image/svg+xml" data="/assets/svg/kubernetes/k8s-pod-sidecar.svg">
<img src="/assets/svg/kubernetes/k8s-pod-sidecar.png" alt="Falling back to PNG as your browser does not support SVG." />
</object>
</figure>
</div>
<div class="download-link">
<a href="/assets/svg/kubernetes/k8s-pod-sidecar.svg">Download SVG</a>
| <a class="download-link" href="/assets/svg/kubernetes/k8s-pod-sidecar.png">PNG</a>
</div>
<hr />
<h2 id="kubernetes-entities-1">Kubernetes entities</h2>
<ul>
<li>Services
<ul>
<li>routed across multiple pods</li>
<li>hides underlying implementation</li>
</ul>
</li>
<li>Route to service
<ul>
<li>ClusterIP</li>
<li>NodePort</li>
<li>Ingress</li>
</ul>
</li>
</ul>
<hr />
<p>Services broker requests to an orchestrated set of pods</p>
<div class="svg-embed">
<figure class="image">
<object class="wrapper" type="image/svg+xml" data="/assets/svg/kubernetes/k8s-service-across-pods.svg">
<img src="/assets/svg/kubernetes/k8s-service-across-pods.png" alt="Falling back to PNG as your browser does not support SVG." />
</object>
</figure>
</div>
<div class="download-link">
<a href="/assets/svg/kubernetes/k8s-service-across-pods.svg">Download SVG</a>
| <a class="download-link" href="/assets/svg/kubernetes/k8s-service-across-pods.png">PNG</a>
</div>
<hr />
<p>Pods may be replicates across multiple worker nodes</p>
<div class="svg-embed">
<figure class="image">
<object class="wrapper" type="image/svg+xml" data="/assets/svg/kubernetes/k8s-service-across-workers.svg">
<img src="/assets/svg/kubernetes/k8s-service-across-workers.png" alt="Falling back to PNG as your browser does not support SVG." />
</object>
</figure>
</div>
<div class="download-link">
<a href="/assets/svg/kubernetes/k8s-service-across-workers.svg">Download SVG</a>
| <a class="download-link" href="/assets/svg/kubernetes/k8s-service-across-workers.png">PNG</a>
</div>
<hr />
<h2 id="kubernetes-cluster">Kubernetes cluster</h2>
<ul>
<li>Master nodes</li>
<li>Worker nodes</li>
</ul>
<hr />
<h2 id="help">Help</h2>
<p>If you’d like help organising and preparing your digital strategy, working with your team to foster a data-driven culture, or just hands-on-keyboard training in infrastructure-as-code, please <a href="/contact">get in touch</a>.</p>
<p><a class="reveal-link" href="reveal/">View as a presentation</a></p>Alex StanhopeStart simple Massively powerful tools Simple to use Complex use cases Focus on the key activities This piece is about a massively powerful set of tools that collectively manage container-based applications. Each of these tools is vast in its power and scope. As such, each can be used in a plethora of different ways. By means of an introduction to each, my goal is to focus on a few functions of each tool, how they work and what they do. Docker Docker runtime Running containers Concurrent but isolated Installation options Dev UAT and Production Docker is not a hypervisor, but it behaves a little bit like one. It allows different workloads to be run side-by-side without interfering with each other. Containers ensure standardisation for distribution and isolation for execution. Docker can be installed for a variety of platforms. In production, or Production-like environments, that’s typically as a package for Linux. Docker interactions Runs containers Entry point Run as 1000:1000 Read-only file system Volumes Port mapping Docker command-line docker run -d --restart=unless-stopped \ -p 80:80 -p 443:443 \ rancher/rancher:latest A single container running on a Docker host Download SVG | PNG Docker build process Create container image: docker build Instantiated container: docker run Connect into container: docker exec -it Stop container: docker stop Delete container: docker rm Delete container image: docker rmi Docker compose Run up multiple containers File-based, YAML Simple to start up docker-compose up Simple to stop them docker-compose down version: "3" services: elasticsearch: image: docker.elastic.co/elasticsearch/elasticsearch:7.5.1 environment: - discovery.type=single-node ports: - 9200:9200 logstash: image: docker.elastic.co/logstash/logstash:7.5.1 ports: - 5044:5044 kibana: image: docker.elastic.co/kibana/kibana:7.5.1 ports: - 5601:5601 depends_on: - elasticsearch A single Docker host running multiple containers side-by-side Download SVG | PNG Kubernetes Container orchestration Networking Services Load balancing High availability Ingress Secret management Kubernetes entities Pod wraps a container Deployment defines pod and how it’s deployed to cluster multiple instances of a pod multiple different pods A Kubernetes pod wrapping a single container Download SVG | PNG A single Kubernetes pod wrapping multiple containers (sidecar pattern) Download SVG | PNG Kubernetes entities Services routed across multiple pods hides underlying implementation Route to service ClusterIP NodePort Ingress Services broker requests to an orchestrated set of pods Download SVG | PNG Pods may be replicates across multiple worker nodes Download SVG | PNG Kubernetes cluster Master nodes Worker nodes Help If you’d like help organising and preparing your digital strategy, working with your team to foster a data-driven culture, or just hands-on-keyboard training in infrastructure-as-code, please get in touch.