<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: STeve Shary</title>
    <description>The latest articles on Forem by STeve Shary (@stevesmashcode).</description>
    <link>https://forem.com/stevesmashcode</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F628843%2F078f5ebe-0e3a-4799-8204-92c1846025b3.jpeg</url>
      <title>Forem: STeve Shary</title>
      <link>https://forem.com/stevesmashcode</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/stevesmashcode"/>
    <language>en</language>
    <item>
      <title>Tips on Building a Docker image (quickly) in a CI/CD Pipeline</title>
      <dc:creator>STeve Shary</dc:creator>
      <pubDate>Thu, 05 Aug 2021 17:50:03 +0000</pubDate>
      <link>https://forem.com/stevesmashcode/tips-on-building-a-docker-image-quickly-in-a-ci-cd-2ph7</link>
      <guid>https://forem.com/stevesmashcode/tips-on-building-a-docker-image-quickly-in-a-ci-cd-2ph7</guid>
      <description>&lt;p&gt;Building docker images has been evolving over the years and there are new strategies to do this.  One of the pain points that comes up frequently is the (lack of) speed when building an image on a CI/CD pipeline.  Many pipelines are now docker containers themselves and are a total blank slate.  We lose out on many optimizations if we don't realize it.&lt;/p&gt;

&lt;p&gt;Below are a series of tips and tricks to make your docker image speed up in your build pipeline.  I have seen build times drop from 10's of minutes to less than 60 seconds, though your miles may vary!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Use &lt;code&gt;.dockerfileignore&lt;/code&gt;&lt;/strong&gt;: The first step of every docker build is that docker will tarball the entire directory and
send the data to docker.  This is output as the line &lt;code&gt;Sending build context to Docker daemon  XX.XXMB&lt;/code&gt;  This should only be
the source code and configuration files that are used in your build.  It really should not be more than 10's of MB.  Make
sure to exclude everything that is not needed.&lt;/li&gt;
&lt;li&gt;Things that don't often change should be pushed up in the file.  The initial package installs, static environment variables,
should be moved up.  Docker has a concept of caching, but the first line in the dockerfile that has a change, all lines
after it will no longer be cached.  The last line of the dockerfile should be adding in your code, or compiling it!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A typical file should have the following flow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;  FROM base-cotnainer

  &lt;span class="c"&gt;# Add all static environment variables, folders, users...&lt;/span&gt;
  ENV IS_APPLICATION=1
  RUN mkdir app
  WORKDIR /app  
  RUN useradd -ms /bin/sh newuser
  COMMAND ["python3", "main.py"]

  # Install all base image packages
  RUN apt install ...

  # Copy over your library depency files (Python example below)
  COPY Pipfile /app/Pipfile
  COPY Pipfile.lock /app/Pipfile.lock

  &lt;span class="c"&gt;# Install your libraries (Python example below)&lt;/span&gt;
  RUN pipenv install --deploy

  &lt;span class="c"&gt;# Lastly, copy the code over&lt;/span&gt;
  COPY . /app/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Don't use a wildcard with the &lt;code&gt;COPY&lt;/code&gt; command.  Docker will never cache that line. Be explicit!&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Build your containers once.  There should be zero reason to build your application multiple times.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Build your containers at the same time.  If you have an application, test, integration, performance, et.al container,&lt;br&gt;
build them all as parallel tasks/steps/jobs in your pipeline.  Much of the docker builds is around I/O tasks and can and should be parallelized.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Copy your library dependency file definitions manually and then run the library install before you copy your source code!  Doing&lt;br&gt;
this step first will allow you cache the pulling of libraries (which feels like a full download of the internet).  Below are&lt;br&gt;
language specific examples of doing this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Javascript&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;pre class="highlight docker"&gt;&lt;code&gt;  FROM [...]
  mkdir /app
  WORKDIR /app
  &lt;span class="c"&gt;# ...&lt;/span&gt;
  COPY package.json /app
  COPY package-lock.json /app
  RUN npm install

  COPY [all your code and configuration]    
&lt;/code&gt;&lt;/pre&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Python (requirements.txt)&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; [...]&lt;/span&gt;
mkdir /app
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="c"&gt;# ...&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; requirements.txt /app&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;pip3 &lt;span class="nb"&gt;install&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; [all your code and configuration]    &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Python (pipenv)&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; [...]&lt;/span&gt;
mkdir /app
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="c"&gt;# ...&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; Pipfile /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; Pipfile.lock /app&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;pipenv &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--deploy&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; [all your code and configuration]    &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Java (Maven)&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; [...]&lt;/span&gt;
mkdir /app
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="c"&gt;# ...&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; pom.xml /app/pom.xml&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;mvn &lt;span class="nb"&gt;install&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; [all your code and configuration]    &lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;mvn package
&lt;/code&gt;&lt;/pre&gt;



&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Java (Gradle)&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Add this to your &lt;code&gt;build.gradle&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight plaintext"&gt;&lt;code&gt;task downloadDependencies(type: Exec) {
configurations.testRuntime.files
commandLine 'echo', 'Downloaded all dependencies'
}
&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;Example Dockerfile to pull dependencies as a cached layer.&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; [...]&lt;/span&gt;
mkdir /app
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="c"&gt;# ...&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; build.gradle /app/build.gradle&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;./gradlew downloadDependencies

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; [all your code and configuration]    &lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;./gradlew build
&lt;/code&gt;&lt;/pre&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;DotNet (Nuget)&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; [...]&lt;/span&gt;
mkdir /app
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="c"&gt;# ...&lt;/span&gt;
&lt;span class="c"&gt;# Copy all solution files .sln (NO WILDCARDS!)&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; WebApp.sln /app/WebApp.sln &lt;/span&gt;
&lt;span class="c"&gt;# COPY all .csproj files (NO WILDCARDS!)&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; WebApp.Application/WebApp.Application.csproj /app/WebApp.Application/WebApp.Application.csproj&lt;/span&gt;
&lt;span class="c"&gt;# Yep.. gotta manually add them all (... Pokemon!)&lt;/span&gt;

&lt;span class="c"&gt;# This command will pull all library &lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;dotnet restore

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; [all your code and configuration]    &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Don't&lt;/strong&gt; pre-pull images.  It's slow and it doesn't work.  Using the new &lt;code&gt;BUILDKIT&lt;/code&gt;
defined below, Docker will be smarter and only pull the image layers that are cached.  This is faster,more
efficient and it works.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;

&lt;p&gt;Use docker's new library &lt;code&gt;BUILDKIT&lt;/code&gt; and inline cache information into your builds.  (This is a simple and big one)&lt;br&gt;
There are three things we need to set to get proper image caching in a pipeline:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Set the &lt;code&gt;DOCKER_BUILDKIT&lt;/code&gt; environment variable to 1&lt;/li&gt;
&lt;li&gt;Add a build argument to tell the new BUILDKIT library to line caching information in each image layer: &lt;code&gt;--build-arg BUILDKIT_INLINE_CACHE=1&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Tell the docker build command where you can get your image from to cache &lt;code&gt;--cache-from [remote-repo-url]/[CONTAINER_NAME]:latest&lt;/code&gt;
The full command would look something like this: &lt;code&gt;DOCKER_BUILDKIT=1 docker build --build-arg BUILDKIT_INLINE_CACHE=1 --cache-from [remote-repo-url]/[CONTAINER_NAME]:latest&lt;/code&gt; --tag [CONTAINER_NAME] .`&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Lastly, don't forget to push an updated image to your remote docker repo!&lt;/p&gt;

</description>
      <category>docker</category>
      <category>python</category>
      <category>dotnet</category>
      <category>java</category>
    </item>
    <item>
      <title>What if I shared everything I know...</title>
      <dc:creator>STeve Shary</dc:creator>
      <pubDate>Fri, 28 May 2021 14:37:35 +0000</pubDate>
      <link>https://forem.com/stevesmashcode/what-if-i-shared-everything-i-know-fen</link>
      <guid>https://forem.com/stevesmashcode/what-if-i-shared-everything-i-know-fen</guid>
      <description>&lt;p&gt;At some point in your career, you will probably sit in a room and realize that you know more about the topic of discussion than anyone else.  It might be some piece of functionality, a current bug, or maybe something larger like an entire system, business model, or technology.  I challenge you to share everything you know.&lt;/p&gt;

&lt;p&gt;Some practitioners view their niche specialty as their ability to have: job security, higher wages, better title or some other intangible prestige.   It's easy to understand.  It's nice to hear these things:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I hear you are the expert at..."&lt;/p&gt;

&lt;p&gt;"Our company would be really screwed if you ever tried to quit here."&lt;/p&gt;

&lt;p&gt;"Wow, without you in these discussions, we could have made a big mistake with our assumption."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And we also have the fear of &lt;a href="https://www.nytimes.com/2015/06/04/us/last-task-after-layoff-at-disney-train-foreign-replacements.html"&gt;being&lt;/a&gt; &lt;a href="https://www.axios.com/trump-att-outsourcing-h1b-visa-foreign-workers-1f26cd20-664a-4b5f-a2e3-361c8d2af502.html"&gt;forced&lt;/a&gt; &lt;a href="https://www.cbsnews.com/news/training-your-own-replacement/"&gt;to share&lt;/a&gt; &lt;a href="https://www.computerworld.com/article/2490610/this-it-worker-had-to-train-an-h-1b-replacement.html"&gt;our knowledge&lt;/a&gt;.  I get it.  I mean if everyone could do my job, I wouldn't be able to have a job.&lt;/p&gt;

&lt;p&gt;When we start our technical careers, we have a small amount of knowledge.  Whether through a degree, certification, or DIY knowledge, we have a glimmer to show that we can work in the technology field.  There is an assumption that new employees, especially new to the field, will require training and ramp-up time.  We have to teach you all the specific stack, development lifecycle, and codebase to be able to do your job.   Your value to the company grows tremendously in the first year.  &lt;/p&gt;

&lt;p&gt;Let's talk about what that value can look like to a company:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xUIzHhph--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qmnz8x565vcmjl6er10a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xUIzHhph--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qmnz8x565vcmjl6er10a.png" alt="New Employee value in different companies"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the x-axis, we have the number of weeks that a new software engineer has been at a company.  The y-axis is value to the company.   We have the grey dotted line, the best employee currently at the company as a baseline.  Finally, we have three different curves representing different scenarios. This diagram is not exact, but is here to illustrate some principles.&lt;/p&gt;

&lt;p&gt;The first is the somewhat common expectation that is baked into many project plans: new software engineers will provide the same level of output/value in a negligible amount of time.  It's obviously wrong, but used much of the time because: it pushes the team to output more, it is difficult to actually calculate individual contribution levels as they onboard, and it looks good to upper management that their newly hired employee was worth the money.&lt;/p&gt;

&lt;p&gt;The second situation is "most companies."  If you think you are not in this group, you probably are.  Here are some hallmarks of the vast majority of companies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Waiting for your equipment to arrive.&lt;/li&gt;
&lt;li&gt;A generic onboarding document/checklist.&lt;/li&gt;
&lt;li&gt;Multi-day get my system setup process.&lt;/li&gt;
&lt;li&gt;Multiple: "How do I get an account to this system?"&lt;/li&gt;
&lt;li&gt;Being given simple tasks to complete on your own that grow in complexity over time.&lt;/li&gt;
&lt;li&gt;Being put on support with and having the anxiety: "Oh my God: who do I call if I get an alert!?!"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The final line is one where an employee quickly provides value, but then can also grow above the current best employee.  I have worked at a number of companies, but only one where I felt like I was in the red curve.  &lt;strong&gt;This is where I was with a team that wanted to share everything that they knew with me.&lt;/strong&gt;  &lt;/p&gt;

&lt;p&gt;I am quickly going to say that sharing everything I know is bounded to the context of your profession.  Not necessarily personal life.  This &lt;em&gt;can&lt;/em&gt; happen in a sharing culture but isn't required.  &lt;/p&gt;

&lt;p&gt;There are many ways that we can share our knowledge.  I'm going to boil it down to three different methods:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Writing it down.&lt;/li&gt;
&lt;li&gt;Sharing it verbally.&lt;/li&gt;
&lt;li&gt;Practicing it together.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Writing It Down
&lt;/h2&gt;

&lt;p&gt;This I feel is the most difficult and provides least amount of value (most of the time).   Writing is difficult.  It requires practice to write well formed sentences.  It is difficult to form them into points, arguments, and ideas.  It is hard to know your audience.  Most of the time, if you publish on the internet, anyone can be your reader.&lt;/p&gt;

&lt;p&gt;Secondly, many finer details take up so much space to explain them that you have gigantic volumes written.  These include: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Technical manuals.&lt;/li&gt;
&lt;li&gt;Textbooks.&lt;/li&gt;
&lt;li&gt;RFCs&lt;/li&gt;
&lt;li&gt;Enterprise software documentation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The vast, vast majority of people don't read these through.  They just don't.  I have written many technical guides, designs and documents that are very thorough and never read. Bleh, what a waste.  &lt;/p&gt;

&lt;p&gt;I think the one shining exception of this is &lt;a href="https://stackoverflow.com/"&gt;https://stackoverflow.com/&lt;/a&gt;.   This is where very precise questions can be asked and then precisely answered.  All of us software engineers live and die by this site.&lt;/p&gt;

&lt;p&gt;Lastly, I think that their is an incorrect assumption that writing it down will make it last forever.  Yes, there are a few timeless classics out there, but most of the time not.  &lt;strong&gt;Most written documentation is out-dated, and incorrect.&lt;/strong&gt;. We move too fast and change too many things.  We write documentation after we do something.  Many people would rather no documentation than incorrect documentation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sharing It Verbally
&lt;/h2&gt;

&lt;p&gt;Verbal sharing I think is an improvement in many ways to written.  It's easier to talk than to write for many people.  We usually talk more than we write.   We often have a single audience, or a group that we know.  Occasionally, we get to speak in front of a larger group that we don't know, but that is more the exception than the norm.&lt;/p&gt;

&lt;p&gt;I like sharing my knowledge verbally because I can also get feedback.  Non-verbal head nods are some, but also questions, comments and affirmations.  I can repeat the same idea in different ways until the audience tells me that they understand.  &lt;/p&gt;

&lt;p&gt;This can also hone my understanding of what I am sharing.  People ask lots of questions that I haven't thought about.  It makes me figure out the answers and grow deeper in my knowledge.&lt;/p&gt;

&lt;p&gt;Some may not like verbal knowledge sharing because it is temporal.  "You would have to repeat it over and over again."  Yes, but that assumption is that knowledge is shared by just one person.  If everyone is doing it, then it spreads more like gossip.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practicing It Together
&lt;/h2&gt;

&lt;p&gt;This is my favorite way to share knowledge with other people.  I find it to be the most rewarding and the most impactful.  Rather than I tell you how to do something, let's do it together.&lt;/p&gt;

&lt;p&gt;I do this in a couple of different ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;pair sessions&lt;/li&gt;
&lt;li&gt;mob sessions&lt;/li&gt;
&lt;li&gt;lead the way&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In a pair session, we just sit down and do something together.  I can "drive" (use the keyboard) or you can.  In longer sessions, we switch out.  I encourage anyone that I am pairing with to ask any question they have.  I get many of these:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Why are you putting that in a different function?"&lt;/li&gt;
&lt;li&gt;"How did you do that?" (Using an IDE function)&lt;/li&gt;
&lt;li&gt;"Why did you start there?"&lt;/li&gt;
&lt;li&gt;"What will that do?"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These don't have to be long, or 100% of the time, but I can take an employee on the first day of their job and pair with them and we can get work done.  If I pair with them, their onboarding grows exponentially.  Also, the majority of the time, they know something I don't.  I get to learn everything you do when we pair.&lt;/p&gt;

&lt;p&gt;In a mob session, we just expand the idea of pairing to just have a larger group work together.  This can seem even crazier to a project manager.  "You mean that the entire team is going to work together on a single ticket!?"  In a mob session, I will define the goal: "We want to build a dashboard for our new feature."  I will typically have two TVs hooked up to computers: one for writing code, and the other for googling any questions (Stack Overflow).  All other laptops are banned.   We pass the keyboards to the right every 5 minutes.  All people in a session must participate.  It forces everyone to pay attention because their turn is going to come up.&lt;/p&gt;

&lt;p&gt;Lastly, I like to lead by doing.  As a lead engineer, it is common for me to start a new team/project.  I will setup the codebase, linting, automated testing, pipeline, deployment, alerting.  All of it.  And I do it with the newly formed team.  &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Need to do a zero-downtime deployment?  &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I'll show you how to do it.   &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Want to know how to develop a SLO/SLI/SLA for your service?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let's do it together!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Want to do something that you have no idea how to do?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I don't know either, but let's figure it out!&lt;/p&gt;

&lt;p&gt;I personally won't stay on a team for longer than 6 months.  Most of the time, we have gotten the major pieces working together.  The first several releases are done. Support has been figured out.  The team dynamics are known.  I just take a step back and let the team do their thing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping it up.
&lt;/h2&gt;

&lt;p&gt;At the end of it, I have plateaued in many of my technical skills.  There are new things to learn here and there, but my value doesn't grow now based on how much technical knowledge I have.  My value grows now by sharing it with others.  The real value growth is that if I can teach others to do what I am doing.  Teaching others to share their knowledge as well makes my value exponential.  If I share 10 things with 10 people this year, and they each share those 10 things with 10 others.  &lt;/p&gt;

&lt;p&gt;My value to a company grows most when I share everything I know.&lt;/p&gt;

</description>
      <category>speaking</category>
      <category>career</category>
    </item>
    <item>
      <title>Why did my Java Web App stop working with MySQL!?!</title>
      <dc:creator>STeve Shary</dc:creator>
      <pubDate>Mon, 10 May 2021 17:40:48 +0000</pubDate>
      <link>https://forem.com/stevesmashcode/why-did-my-java-web-app-stop-working-with-mysql-1oig</link>
      <guid>https://forem.com/stevesmashcode/why-did-my-java-web-app-stop-working-with-mysql-1oig</guid>
      <description>&lt;p&gt;There are different kinds of bugs and they show up in different places.  This bug was hard because it showed up where we didn't expect it.  &lt;/p&gt;

&lt;p&gt;We noticed last week that our Java web app deployments to kubernetes (k8s) stopped working last week.  It started with a smaller team that did a deployment (to non-prod).  The error started with a failed deployment.  We use liveliness and readiness probes to help k8s let us know if the application is running correctly.  Our liveliness probe was failing.  That's strange.  It just tells us if we can say "hello" back on a http GET.&lt;/p&gt;

&lt;p&gt;Our first idea is to look at our logs.  We saw the error:&lt;br&gt;
&lt;br&gt;
 &lt;code&gt;The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server.&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;Hmmm... That's weird.  The stack trace came from our MySQL client connection.  There were no changes to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The code that connects to MySQL.&lt;/li&gt;
&lt;li&gt;The client library version.&lt;/li&gt;
&lt;li&gt;The MySQL instance.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We reverted the commit, and wanted to see if going back to the previous commit would allow us to deploy.  Nope.  So now we have the "same" code running in a docker container running on the same deployment running against the SAME database.&lt;/p&gt;

&lt;p&gt;This is the part in a show where the director speeds up time to show that the cast is doing A LOT of work but doesn't want to spend the time going through each minute detail.  The same is here for us.  We started trying every conceivable library and change that we could.   (Fast forward several hours...)&lt;/p&gt;

&lt;p&gt;We then went back and revisited the error:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;code&gt;The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server.&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;It didn't make sense to us and so I did what I normally do not do.  I scrolled through the 5,000+ lines of stack trace "caused by" lines to the bottom of the pit.  At the bottom, I found this message: &lt;code&gt;Caused by: javax.net.ssl.SSLHandshakeException: No appropriate protocol (protocol is disabled or cipher suites are inappropriate)&lt;/code&gt;. This was our best breadcrumb yet!  This indicated that there was a problem establishing a secure connection.   Hmmm...&lt;/p&gt;

&lt;p&gt;One point that you have to understand is that Java handles TLS (a.k.a SSL) connections in the JVM.  When you open a connection to another service, the JVM does all of the secure connection setup.&lt;/p&gt;

&lt;p&gt;So I asked the question: "Has the Java JVM/JRE changed recently?"   The answer was: "yes."  We found that a change to the JVM happened to make the allowed TLS versions more strict: &lt;a href="https://bugs.openjdk.java.net/browse/JDK-8254713"&gt;https://bugs.openjdk.java.net/browse/JDK-8254713&lt;/a&gt;.  &lt;/p&gt;

&lt;p&gt;You see our services run using a docker container.  We have based our docker container off the current LTS of Java: &lt;code&gt;gcr.io/distroless/java:11&lt;/code&gt;. What we didn't think about is that this is not a specifically pinned version.   This means that the docker repo maintainers will periodically updated it.   Turns out that they did after the latest dot release of Java 11.  We were able to fix this immediately by changing the base image to: &lt;code&gt;openjdk:11.0.8-jre&lt;/code&gt;. This pins to specific release of the JVM.  We also put out a request to upgrade our TLS to our database.&lt;/p&gt;

&lt;p&gt;Well, I hope if you see this, that it will be more obvious and take less time to drive to the solution!&lt;/p&gt;

</description>
      <category>java</category>
      <category>security</category>
      <category>docker</category>
      <category>mysql</category>
    </item>
  </channel>
</rss>
